바닐라 자바스크립트로 깃허브 OAuth 를 구현해보자
개발 블로그 개발 여정
reactnextjs
Sat Jun 22 2024
이 글은 개발 블로그 개발 여정의 게시글이예요
해당 포스트는 NextJS를 이용하여 개발 블로그를 만들며 작성한 포스트입니다.
기술 블로그의 전체 코드는 🪢 yonglog github 에서 확인 하실 수 있습니다.
- 1 . 개발블로그 제작을 시작하며
- 2 . CI/CD 파이프라인 구성하기
- 3 . tailwind 환경 설정 및 디자인 레퍼런스 찾기
- 4 . / 경로 레이아웃 , 페이지 디자인 생성하기
- 5 . [BUG] Build 시 발생하는 typeError 해결하기
- 6 . MDX를 사용하기 위한 라이브러리들 설치 및 환경 설정
- 7 . Post들이 저장된 FileSystem 파싱하기
- 8 . PageNavigation UI 만들기
- 9 . tag , series Identifier 만들기
- 10 . / 경로 UI 디자인 하기
- 11 . 서버 컴포넌트에 Dynamic Routing 추가하기
- 12 . Post/page.tsx 생성하기
- 13 . 마크다운 파일 코드블록 꾸미기
- 14 . [BUG] Vercel 에 환경 변수 추가하기
- 15 . Loading Suspense 구현하기
- 16 . generateStaticParams 이용해 SSR 에서 SSG로 넘어가자
- 17 . SSG를 이용한 블로그에서 테마 변경하기
- 18 . 인터렉티브한 사이드바 만들기
- 19 . 기술블로그에 giscus를 이용하여 댓글 기능을 추가하기
- 20 . 기술 블로그의 SEO를 최적화 하기 위한 방법 Part1
- 21 . 기술 블로그의 SEO를 최적화 하기 위한 방법 Part2
- 22 . 바닐라 자바스크립트로 깃허브 OAuth 를 구현해보자
- 23 . 라이브러리 없이 깃허브 API를 이용해 댓글창을 구현해보자
- 24 . 리팩토링 : Promise 패턴을 이용하여 비동기 처리중인 전역 객체에 접근하기
- 25 . NextJS로 만든 기술블로그에서 검색 기능 구현하기
현재는 giscus 라이브러리를 이용하여 댓글을 사용하고 있다.
현재는 giscus를 이용하고 있다.
현재는 giscus
를 이용하여 깃허브 OAuth
및 댓글을 사용하고 있다.
giscus
는 깃허브 API 를 활용하여 디스커션에 댓글을 달고, 해당 디스커션에 존재하는 댓글 리스트를 OAuth
과정에서 얻은 액세스 토큰을 이용해 가져오는 라이브러리이다.
라이브러리를 이용하면 한 시간도 안돼서 빠르게 댓글 기능을 구현 할 수 있었으나 공부를 위해 기술 블로그를 만들면서 라이브러리를 사용하는 것은 아닌 것 같아서 바닐라 자바스크립트로 직접 구현하려고 한다.
OAuth
에 대한 내용은 🪢 이전 글을 참고하도록 하자 :)
전체적인 Github OAuth의 다이어그램
Github API를 이용한 OAuth의 다이어그램
전체적인 다이어그램은 다음과 같다.
OAuth 를 이용해 로그인을 하게 되면 다음과 같을 일들이 벌어진다. 화살표를 기준으로 좌측은 요청 주소 , 우측은 응답 주소이다.
- 사용자가 로그인 버튼을 누른다.
- 사용자는 깃허브 로그인 페이지로 리다이렉트 된다. (웹페이지 -> 깃허브 인증 엔드포인트)
- 사용자는 로그인을 시도하여 인증을 시도하고 인증에 성공할 경우 임시 코드를 발급 받으며 임시 코드와 함께 abonglog 엔드 포인트로 리다이렉트 된다. (깃허브 인증 서버 -> abonglog 엔드 포인트)
- 임시 코드를 이용해 깃허브 인가 서버에게 액세스 토큰을 요청한다. (abonglog 엔드포인트 -> 깃허브 인가 엔드포인트)
- 발급 받은 액세스 토큰을 사용자에게 쿠키에 담아 보낸다. (abonglog 엔드포인트 -> 웹페이지)
인증 서버로부터 client_id , client_secret 발급 받기
settings / developer settings/OAuth app 에서 발급 가능하다.
우선 깃허브 인증 서버로부터 OAuth 를 사용 할 application-name 을 등록 하고 client_id , client_secret
을 발급 받는다.
client_id , client_secret
모두 리소스 오너에게 액세스 토큰을 발급 받게 할 때 필요하기 때문에 환경 변수에 저장해주도록 하자
Authorization callback URL
이후 하단에 callback URL
을 설정해주도록 하자 :)
도메인은 현재 개발 중이기 때문에 localhost
로 해뒀지만 배포 시에 사용 하려면 나의 도메인을 사용 하면 된다.
중요한 점은 /api/OAuth
경로인 것인데 해당 callback URL
은 사용자가 인증 서버에서 인증을 마친 후 redirect
될 URL
을 의미한다.
해당 내용은 추후 코드와 함께 자세히 설명하도록 하겠다.
Login 컴포넌트 생성하기
해당 컴포넌트는 별게 없다. 그저 단순히 깃허브 로그인 페이지로 리다이렉트 시키는 a
태그 (현재 컴포넌트에선 Link
컴포넌트가 사용 되었다.)만 존재한다.
이 때 쿼리 파라미터로 client_id , state , redirect_uri , scope
등을 담아 보내주었다.
쿼리 파라미터에 대한 정보는 🪢 Github Docs - Authorizing OAuth apps#1. Request a user's GitHub identity 에서 자세히 살펴볼 수 있다.
필수적으로 존재해야 하는 쿼리 파라미터는 client_id
이다. client_id
는 깃허브 인증 서버로부터 해당 OAuth
서비스를 이용하는 도메인이 누구인지 자격 증명 하는데 사용된다.
나머지 쿼리 파라미터들은 다음과 같다.
state
:cross-site request forgery attacks
를 막기 위한 랜덤한 문자열 등을 담아 사용한다. 특징적인 것으론 인증 이후 리다이렉트 될 때 임시 코드와 함께 쿼리파라미터에 담겨 온다.
나는 사용자를 로그인을 시도한 게시글로 리다이렉트 시키기 위해 state
에 난수 값과 함께 현재 머물고 있는 주소의 정보도 같이 담아주었다.
redirect_uri
: 인증 서비스 이후 리다이렉트 시킬uri
주소를 의미한다. 깃허브 인증서버는OAuth app
을 생성 할 때 등록한redirect_uri
와 같지 않다면 오류를 발생 시키며 쿼리파라미터에 담아 작성하지 않을 경우OAuth app
을 생성 할 때 등록한 주소로 리다이렉트 시킨다.scope
: 사용자가 발급 받아 인가에 사용 할access token
의 범위를 의미한다. 나의 경우엔 내 레파지토리public_repo
와 디스커션들에 대해 쓰고 읽을 수 있도록 해주었다.
로그인 컴포넌트
해당 컴포넌트를 클릭하면 사용자는 하단의 사진과 같은 깃허브 로그인 사이트로 이동 되며 해당 사이트에서 로그인을 시도하게 된다.
리다이렉트 된 깃허브 로그인 페이지
이게
OAuth
의 가장 큰 장점이다. 나같이 게으른 개발자 입장에선 로그인을 위한 데이터베이스를 만들지 않아도 되고 사용자 입장에선 이미 있는 계정으로 로그인 하면 되니 얼마나 간편한가 ! 심지어 내 계정에 대한 정보를 직접적으로 깃허브에게만 제공하면 되니 안심이 된다.
api/OAuth 엔드포인트 라우터 핸들러 생성하기
사용자가 만약 깃허브 로그인 페이지에서 로그인을 시도하고 Authorize
버튼을 클릭해 토큰을 발급 받기를 동의하였다면
깃허브 로그인 페이지는 redirect_uri
쿼리 파라미터로 제공한 주소로 액세스 토큰을 발급 받을 수 있는 임시 코드 값과 입력했던 state 값을 request url
에 담아 리다이렉트 한다.
나는 /api/OAuth
로 리다이렉트 시키기로 했기 때문에 /api/OAuth
엔드포인트에 대한 라우트 핸들러를 생성해주도록 하자
/api/OAuth
엔드포인트에 대한 GET
요청 라우트 핸들러를 생성해줬다.
해당 코드에선 깃허브 로그인 페이지에서 보낸 request
에 접근해 code , state
값을 추출하여 저장하고 발급 받은 code
값을 이용해 https://github.com/login/oauth/access_toke
경로로 임시 코드 값과 client_id , secret
값을 담아 POST
요청을 보낸다.
포스트 요청 시 사용 할 POST 요청에 대한 자세한 정보는 🪢 Github Docs - Authorizing OAuth apps#2. Users are redirected back to your site by Github 를 참고하도록 하자 :)
POST
요청 이후엔 다음과 같은 응답 값을 받을 수 있다.
응답 값에서 access_token
값만 쏙 빼내어 클라이언트에게 보낼 response
쿠키에 담은 후 사용자를 다시 리다이렉트 시켜주었다.
결국 사용자는 로그인 페이지에서 로그인을 성공 하기만 하면 access token
을 쿠키에 담은 채로 본래 보던 페이지로 리다이렉션 되게 된다. :)
본래 보던 페이지로 리다이렉션 시키기 위해서
state
쿼리 파라미터에 저장해두었던postId
를 이용해주었다.
클라이언트에서 access token 관리하기
그럼 앞으로 사용자가 access token 이 필요한 요청을 수행 할 때 마다 쿠키에 access token 을 담아 전송하기 때문에 리소스를 사용하는데 있어 필요한 인가와 인증 문제는 처리가 되었다.
그럼 이제 쿠키에 저장 된 access token을 상태 값으로 저장해둬 access token 유무에 따라 로그인 , 로그아웃 상태를 정의해주도록 하자
쿠키를 관리하는 메소드 정의
쿠키들을 다루는 getCookie , deleteCookie
메소드를 정의해주었다.
기존엔 cookieStore
를 이용해줬었는데 fireFox,safari
에선 쿠키 스토어가 사용 불가능 하단 사실을 알고 단순히 document.cookie
를 이용해주었다.
쿠키를 제거하는 deleteCookie
메소드의 경우엔 쿠키를 삭제 하는 방법으로 쿠키의 만료 기간을 과거의 시간으로 설정해 쿠키를 제거해주도록 하였다.
쿠키에 접근하여 상태 값으로 정의하기
나는 로그인 , 로그아웃 서비스를 댓글 기능에서 정의 할 것이기 때문에 댓글과 관련된 컴포넌트에 상태 값으로 정의해주었다.
CommentForm 컴포넌트의 모습
초기 token
의 상태 값을 getCookie
메소드로 쿠키에서 꺼내와 정의해주고 토큰 유무에 따라 로그인, 로그아웃 기능이 있는 Login , Logout
컴포넌트를 조건부적으로 렌더링해주었다.
로그아웃 컴포넌트도 단순히 쿠키 값을 제거하고 token
값을 null
로 변경해주었다.
이번 포스팅은
OAuth
와 관련된 내용에만 집중하기 위해서 전체적인 컴포넌트 구조에 대한 설명은 하지 않았다. 만약 전체 코드가 궁금하다면 깃허브에 업로드 해둔 🪢 @/component/client/Comment.tsx 파일을 살펴보면 좋을 것 같다.