주니어 개발자의 우여곡절 소셜로그인 도입 과정

안녕하세요, 오픈소스 기술파트에서 근무하고 있는 rogers라고 합니다. 오픈소스 기술파트에서는 오픈소스 관리 서비스인 OLIVE Platform을 제공하고 있습니다. 이번에 더 많은 사용자에게 OLIVE Platform을 제공하기 위해 구글, 깃허브, 페이스북 소셜로그인을 도입하게 되었습니다. 

이 글에서는 앱 등록부터 소셜로그인을 구현하는 과정뿐만 아니라 소셜로그인 연동을 해제하는 방법도 설명합니다. 그리고, 프락시 문제, API 사용 제한사항(요청수 제한, 로고 추가로 인한 앱 검수) 등 적용 과정에서 겪은 시행착오와 해결 방법도 확인하실 수 있습니다.

용어 설명부터 드릴게요!

구글, 깃허브, 페이스북은 OAuth2 프로토콜 기반의 인증방식을 지원하는데요. OAuth2 주요 용어에 대해 먼저 설명해 드리고 소셜로그인 적용 과정에 대해 말씀드리겠습니다.

용어

설명

클라이언트(Client)

리소스에 접근하려는 애플리케이션에 해당합니다. OLIVE Platform에서는 Spring 서버에 해당합니다.

인증 서버(Authorization Server)

인증 및 인가를 수행하는 서버로 리소스에 대한 액세스 토큰을 발행합니다.

리소스 서버(Resource Server)

리소스를 호스팅하는 서버로 액세스 토큰의 유효 여부를 확인하고 해당 리소스에 대한 접근을 허용합니다.

인증 코드(Authorization Code)

사용자가 로그인에 성공하고 나서 받는 코드이며 이후 액세스 토큰을 발행할 때 필요합니다.

액세스 토큰(Access Token)

리소스에 접근할 때 필요한 토큰입니다.

클라이언트 ID(Client ID),

클라이언트 보안 비밀번호(Client Secret Password)

등록된 클라이언트에게 발급하는 정보로 인증하는 데 사용됩니다.

권한 범위(Scope)

리소스 접근 범위를 의미합니다.

리다이렉트 URI(Redirect URI)

인증 서버가 인증 후 응답을 보낼 클라이언트 URI에 해당합니다.

 

앱 등록

앱 등록이란 앱에 대한 정보를 입력하고 클라이언트 ID 및 클라이언트 보안 비밀번호(Client Secret Password)를 얻는 과정을 말합니다. 여기에서 앱 등록 과정은 페이스북으로 진행하며, 플랫폼별로 방식은 조금씩 다르므로 이 부분은 참고 부탁드립니다. 

메타(이하, “페이스북”으로 통칭)에서 앱 등록을 하기 위해서는 페이스북 계정과 Meta for Developers 계정, 그리고 앱의 페이스북 페이지가 필요합니다. 다음에서는 Meta for Developers 계정에 로그인한 이후의 앱 등록 절차를 소개하고 있습니다.

1. Meta for Developers내 앱에 접속한 후 앱 만들기 버튼을 눌러줍니다.

2. 앱 유형을 비즈니스로 선택합니다.

3. 앱 이름이메일을 입력합니다.

4. 설정 > 기본 설정에서 추가 정보를 입력합니다.

5. 대시보드 탭으로 이동한 후 Facebook 로그인의 설정을 클릭합니다.

6. 을 선택합니다.

7. 웹사이트 정보를 입력 후 계속 다음하기를 눌러 마지막 장까지 갑니다. 웹사이트 정보 입력란 외의 나머지 페이지들은 구현 가이드입니다.

8. Facebook 로그인 탭의 설정에 들어갑니다. 유효한 OAuth 리디렉션 URI를 입력합니다. 이 URI는 인증 서버로부터 응답을 받는 주소가 됩니다.

9. 앱 검수 탭의 권한 및 기능으로 들어갑니다. 필요한 권한(Scope)이 있다면 고급 액세스 이용하기를 눌러 권한을 얻습니다.

10. 설정 탭의 기본 설정에 들어가서 앱 ID앱 시크릿 코드를 복사합니다. 앱 ID와 앱 시크릿 코드는 이후 인증과정에서 필요한 정보입니다.

Spring Framework 라이브러리 설정

OLIVE Platform의 Spring 서버에 OAuth2 인증 과정을 쉽게 처리해주는 spring-boot-starter-oauth2-client 라이브러리를 도입하여 서버 단의 소셜로그인을 구현했습니다.

의존성 추가

spring-boot-starter-oauth2-client를 사용하기 위해서는 의존성을 추가해줘야 합니다.

				
					 implementation("org.springframework.boot:spring-boot-starter-oauth2-client")

				
			

Client Registration 및 Provider 주요 설정

의존성을 추가한 후 아래 사진과 같이 Client Registration 및 Provider 설정을 추가해줘야 합니다.

Client Registration 부분에서는 클라이언트의 정보를, Provider 부분에서는 인증 서버와 리소스 서버에 대한 정보를 추가합니다. 

구글, 깃허브, 페이스북의 경우 기본 설정들이 미리 정의되어 있어 clientId, clientSecret, scope 설정만 추가해주면 됩니다. 

OLIVE Platform에서는 Spring의 각종 설정을 사내 키 관리 서비스에 보관해서 사용하고 있습니다. 따라서 직접 빈(Bean)으로 등록할 필요성이 있었습니다. 공식 가이드를 참고하여 빈으로 등록할 수 있었습니다.

 

Oauth2 설정 커스터마이징

아래 코드처럼 OAuth 2.0 로그인 설정(Configuration)에 대해 커스터마이징할 수 있는데요. 직접 커스터마이징한 설정들에 대해 설명해 드리도록 하겠습니다.

				
					@Configuration

@EnableWebSecurity

public class OAuth2LoginSecurityConfig {

@Bean

public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

http

.oauth2Login(oauth2 -> oauth2

    .authorizationEndpoint(authorization -> authorization

    .baseUri("/oauth2/authorization")

.authorizationRequestRepository(this.authorizationRequestRepository())

  )

.redirectionEndpoint(redirection -> redirection

.baseUri("/*/oauth2/code/*")

)

.userInfoEndpoint(userInfo -> userInfo

.userService(this.oauth2UserService())

)                         

.successHandler(oAuth2AuthenticationSuccessHandler())

.failureHandler(oAuth2AuthenticationFailureHandler())

);

return http.build();

}

}

				
			

다음의 5가지 설정을 커스터마이징했습니다.

  1. authorizationEndpoint: 사용자가 호출하는 클라이언트의 인증 시작 API에 대한 설정입니다. 사용자가 이 API를 호출하면 소셜로그인 페이지로 사용자를 리다이렉트합니다.
    • authorizationRequestRepository: 사용자의 인증 요청을 임시로 보관하는 리포지토리에 대한 설정입니다. 이 요청에는 인증 과정을 모두 마친 후 리다이렉트할 프론트의 URI가 담겨있습니다. 
  2. redirectionEndpoint: 인증 서버가 응답을 반환하는 클라이언트의 URI에 대한 설정입니다. /login/oauth2/code/facebook처럼 매칭됩니다.
  3. userInfoEndpoint: 리소스 서버로부터 유저 정보를 가져올 때 사용되는 설정입니다.
  4. successHandler: 인증 및 유저 정보를 가져오는 것까지 성공했을 때 호출되는 핸들러를 설정합니다.
  5. failureHandler: 인증 또는 유저 정보를 가져오는 데 실패했을 때 호출되는 핸들러를 설정합니다.

 

JWT 도입

OLIVE Platform에서는 마이 페이지, 내 프로젝트 등 로그인 및 권한이 있는 상태에서 이용할 수 있는 기능들이 있는데요. 이런 기능들을 제공하기 위해 로그인 상태를 유지할 필요가 있습니다. 로그인 상태를 유지하기 위해 JWT(JSON Web Token) 토큰을 이용하였습니다.

서버에는 다음의 두 가지 로직을 구현하였습니다.

  1. 소셜로그인 인증 및 유저 정보를 가져오는 데 성공하면 successHandler로 진입하는데요. 이 핸들러에 JWT 토큰을 발급하고 이를 포함시켜 프론트로 리다이렉트하도록 했습니다.
  2. JWT 토큰을 받은 프론트는 다음 API 요청부터 토큰을 포함시켜 보내는데요. 필터에서 해당 토큰이 유효한지와 해당 사용자가 존재하는지 확인합니다. 확인에 성공하면 SecurityContextHolder UserPrincipal을 저장했습니다. 저장된 UserPrincipal은 이후 로그인 여부 파악 등에 사용됩니다.

 

프론트 작업

이렇게 서버 구현을 하고 나서 프론트 작업을 추가로 진행해줘야 하는데요. 크게 2가지를 구현했습니다.

1. 먼저 클릭했을 때 인증 시작 API로 이동하는 로그인 버튼을 추가했습니다. 링크는 “{client url}/oauth2/authorization/facebook?redirect_uri=’프론트의 콜백 URI’”로 작성했습니다.

2. 클라이언트에서 인증과정을 모두 마치면 1번 과정에서 파라미터로 넘긴 redirect_uri로 리다이렉트해 라우팅 등의 추가 구현을 합니다.

    • 로그인에 실패하면 로그인 실패 페이지로 이동합니다.
    • 로그인에 성공하면 서버로부터 받은 JWT 토큰을 저장합니다. 이후 클라이언트 API를 호출할 때 이 토큰을 함께 보내게 됩니다.
    • 신규 유저면 회원가입 페이지로, 기존 유저면 내 프로젝트 페이지로 이동합니다.

 

이렇게 프론트 구현까지 마치게 되면 OAuth2를 이용한 소셜로그인 구현은 끝나게 됩니다.

 

전체 시퀀스 다이어그램

지금까지 구현한 OAuth2 소셜로그인에 대한  전체 시퀀스 다이어그램입니다.

 

 

  1. 사용자의 유저 에이전트(User Agent)가 클라이언트(Client)의 인증 시작 API를 호출합니다.
  2. 클라이언트는 유저 에이전트를 페이스북의 인증 URI(Authorization URI)로 리다이렉트합니다.
  3. 사용자는 로그인을 수행합니다.
  4. 인증 서버(Authorization Server)가 인증 성공 여부를 클라이언트로 전달합니다. 성공 시 인증 코드(Authorization Code)가 전달됩니다.
  5. 클라이언트는 액세스 토큰(Access Token)을 요청합니다. 이전에 전달받은 인증 코드를 파라미터로 포함합니다.
  6. 인증 서버는 클라이언트의 요청이 유효하다면  액세스 토큰을 반환합니다.
  7. 클라이언트는 사용자의 프로필 정보를 요청합니다. 이전에 반환받은 액세스 토큰을 파라미터로 포함합니다.
  8. 인증 서버는 클라이언트의 요청이 유효하다면 유저 프로필 정보를 반환합니다.
  9. 클라이언트는 JWT 토큰을 발행하고 이 토큰을 포함시켜 프론트 콜백 URI(Front Callback URI)로 리다이렉트합니다.
  10. 프론트(Front)는 JWT 토큰을 저장하고 이후 작업을 수행합니다. 로그인 성공 시 OLIVE Platform의 My Project로 라우팅합니다.

 

아직 끝이 아니다!

소셜로그인 구현은 되었지만, 적용과정에서 다양한 시행착오를 겪었는데요. 이를 해결하는 과정이 다른 분들께 도움이 되길 바라며 자세히 설명하겠습니다. 

 

소셜로그인 연동 해제

OLIVE  Platform은 회원 탈퇴 시 해당 정보를 모두 삭제하는 정책을 따르고 있는데요. 이때 소셜로그인 연동도 같이 해제되어야 합니다. 하지만 spring-boot-starter-oauth2-client 라이브러리는 이 기능을 따로 제공하고 있지 않는데요. 따라서 직접 구현할 필요가 있었습니다.

소셜로그인을 제공하는 서비스들은 권한을 회수하는 API를 제공하고 있습니다. 이 API를 직접 호출하여 소셜로그인 연동 해제를 구현하였습니다.

 

사진을 못 가져오고 있어요! 

로컬 환경에서 개발한 작업을 샌드박스 환경으로 배포해 테스트를 진행했는데요. 페이스북 로그인의 경우 프로필 사진을 못 가져오는 이슈가 있었습니다. 로컬 환경에서는 프로필 사진을 잘 가져오고 샌드박스 환경에서도 로그인 및 닉네임을 가져오는 부분은 잘 동작하여 원인을 찾는 데 어려움을 겪었습니다. 

그렇게 원인을 찾아보다가 페이스북의 프로필 사진 API를 호출하면 자동으로 리다이렉트한다는 사실을 알게 되었습니다. redirect 값을 false로 하여 API를 호출해보았습니다. redirect 값을 false로 하면 아래 사진처럼 실제 사진이 저장되어 있는 URL이 나오게 됩니다.


이 URL은 접근이 거부된 외부 API의 URL과 일치했습니다.

해당 도메인에 대한 접근을 허용하도록 프락시 정책을 수정하였고 이후 성공적으로 프로필 사진을 가져오는 것을 확인할 수 있었습니다.

 

페이스북 API 요청수 제한(Rate Limit) 문제

샌드박스 환경에서 테스트를 진행하던 중 갑자기 페이스북 로그인 및 회원가입이 되지 않는 문제가 발생했습니다. 다른 소셜로그인은 정상적으로 수행되고 페이스북 로그인만 문제가 되는 상황이었습니다. 

로컬 환경에서 디버깅 및 페이스북 앱 설정을 확인해봐도 문제가 없어 보여 원인 파악에 애를 먹었는데요. 좀 더 명확하게 로깅을 찍도록 설정하고 나서 원인을 파악할 수 있었습니다.

페이스북은 최대 1시간 동안 앱의 전체 사용자 수 * 200 만큼의 API 요청을 허용하고 있습니다. 기존에는 사용자의 정보를 얻기 위해 로그인 및 회원가입 외에 다른 기능을 수행할 때도 페이스북 API를 호출하고 있었는데요. 계속 테스트를 진행하며 이 한계를 넘어 API 접근이 제한되었던 것이었습니다. 로그인 및 회원 가입할 때만 페이스북 API를 호출하도록 변경하여 문제를 해결할 수 있었습니다.

 

프로덕션 모드로 전환

보통 소셜로그인 서비스들은 개발자 모드 및 프로덕션 모드 2가지 모드를 제공합니다. 개발자 모드에서는 테스트 계정만 소셜로그인 기능을 사용할 수 있으므로 실제 유저가 사용하기 위해서는 프로덕션 모드로 전환은 꼭 필요한 작업입니다.

OLIVE Platform은 프로덕션 모드로 먼저 전환하여 테스트를 진행하고 있었지만, 앱 로고를 추가하면서 구글 앱 검수가 추가로 필요했습니다. 이에 앱 검수 절차를 밟아나가기 시작했습니다.

1. OAuth 동의 화면의 정보를 채웠습니다. 이 정보들은 사용자가 구글 로그인을 했을 때 나오는 정보들입니다.

2. 다음 페이지로 이동하여 필요한 범위(Scope)를 확인하고 선택적 정보를 입력한 후 계속합니다.

3. 최종 검토 페이지에서 정보가 제대로 입력되었는지 확인 후 제출합니다. 제출을 완료하면 다음과 같이 접수되었다는 안내가 나옵니다.

4. 검토 절차가 진행되면서 필요한 조치가 있으면 3번에서 본 화면에 나오게 되는데요. 도메인 소유권 확인이 필요하다고 나와 이를 진행하게 되었습니다.

5. 구글 서치콘솔 사이트로 이동하여 1번 과정에서 입력한 도메인을 입력합니다.

6. 도메인이 인증되지 않았다면 다음과 같은 팝업창이 뜨게 됩니다. TXT 레코드를 복사하여 DNS 설정에 추가한 후 다시 인증을 시도합니다.

7. 이후 검토 절차를 모두 완료하여 안내 메일과 함께 다음처럼 화면이 변경되었습니다.

8. 구글 로그인 화면에서도 정상적으로 입력한 정보들이 나오는 것을 확인할 수 있었습니다.

 

후기

이런 과정을 거쳐 OLIVE Platform은 카카오, 구글, 깃허브, 페이스북의 총 4가지 로그인을 제공하게 되었습니다. 기획부터 시작해서 실제 서비스 배포까지 한 사이클을 과정을 겪었던 좋은 경험이었습니다. 이 과정에서 제가 얻을 수 있었던 점은 다음과 같습니다.

  • 개발 문서와 좀 더 친해졌습니다. 영어 공부를 해야겠다는 생각도 많이 들었습니다.
  • 버그를 재현하고 원인을 파악하는 능력이 향상되었습니다.
  • 개발을 진행할 때 좀 더 세세한 계획을 수립하는 습관을 들일 수 있었습니다.
  • 작은 단위로 개발, 배포하는 것이 중요하다는 것을 깨달았습니다.

물론 아쉬웠던 점들도 있었는데요. 다음과 같습니다.

  • 리팩토링하는 과정에서 기존 테스트를 보완하거나 추가하는 것이 필요하다.
  • 개발하고 테스트하고 수정하는 과정에서 놓치는 부분이 있었다. 놓치지 않도록 계속 체크리스트를 업데이트할 필요가 있었다.
  • 개발 문서를 처음 접할 때 놓치는 부분이 있었다. 놓치는 부분이 없도록 여러 번 정독하는 습관이 필요하다.
  • 프로덕션 모드를 적용할 때 앱 로고 등을 좀 더 빨리 적용해야 했다. 

 

이번 프로젝트를 수행하면서 많은 것들을 경험하고 성장할 수 있었습니다. 소셜로그인을 도입하기 위해 이 글을 읽는 분들께 도움이 되었으면 좋겠습니다. 긴 글 읽어주셔서 감사합니다.

마지막으로 꼼꼼하게 테스트해 주시고 피드백과 도움을 주신 파트원들, 그리고 같이 프로젝트를 진행하며 많은 가르침을 주신 yally에게 감사드립니다.

 


참고

카카오톡 공유 보내기 버튼

Latest Posts

대학생 멘토링: 우물 밖 내다보기

https://www.youtube.com/watch?v=LGdb4Q5t-gw 안녕하세요. 저는 카카오톡 톡플랫폼개발팀에서 백엔드 서버 개발자로 일하고 있는 nano.son(손은호)입니다. 2023년 1월 16일, 제 모교인 경북대학교의 학생들과 판교 아지트에서 멘토링을 진행했습니다.