본문 바로가기
React/React 실습

[OAuth2] 기존 시스템에 네이버 소셜 로그인 기능 연동하기 (JWT + 팝업 방식)

by haheehee 2025. 4. 2.
728x90

✅ 프로젝트 목표

기존 이메일/비밀번호 로그인 시스템이 있는 웹 애플리케이션에 네이버 소셜 로그인(OAuth2) 기능을 추가합니다. 로그인 후 네이버 계정을 통해 별도 회원으로 가입되며, JWT 방식으로 인증 처리됩니다.

✅ 전체 동작 시나리오

  1. 사용자가 "네이버 로그인" 버튼 클릭
  2. 팝업 창으로 네이버 로그인 화면으로 이동
  3. 로그인 성공 후, 등록된 redirect_uri로 codestate 전달
  4. 백엔드에서 access_token 요청 → 사용자 정보 요청
  5. 네이버 UID로 사용자 조회 → 없으면 자동 회원가입
  6. JWT 발급 → 팝업창에서 부모창으로 전달
  7. 부모창은 토큰 저장 후 로그인 상태 처리

✅ 핵심 처리 방식

  • naverId: 네이버에서 제공하는 비식별 UID로, 계정 식별자로 사용됨
  • 이메일: 사용자 정보 중 하나지만, 로그인 식별자로 사용하지 않음
  • 기존 회원과의 중복 여부: 이메일 같아도 절대 자동 연결하지 않음. 별도 소셜 계정으로 등록

✅ 프론트: 네이버 로그인 요청

const NAVER_CLIENT_ID = '발급받은 client_id';
const NAVER_CALLBACK_URL = 'https://yourdomain.com/api/auth/naver/callback';
const state = crypto.randomUUID();

const loginUrl = \`https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=\${NAVER_CLIENT_ID}&redirect_uri=\${encodeURIComponent(NAVER_CALLBACK_URL)}&state=\${state}\`;

const popup = window.open(loginUrl, 'naverLoginPopup', 'width=500,height=600');

window.addEventListener('message', (event) => {
  if (event.origin !== 'https://yourdomain.com') return;
  const { token } = event.data;
  if (token) {
    localStorage.setItem('access_token', token);
    // 로그인 완료 처리
  }
});

✅ 백엔드: 콜백 처리 및 사용자 등록

public async Task<IResult> HandleNaverCallbackAsync(string code, string state, string origin, string host)
{
    var redirectUri = $"https://{host}/api/auth/naver/callback";
    var tokenUrl = $"https://nid.naver.com/oauth2.0/token?grant_type=authorization_code&client_id=...&client_secret=...&code={code}&state={state}&redirect_uri={Uri.EscapeDataString(redirectUri)}";

    var client = _httpClientFactory.CreateClient();
    var tokenResponse = await client.GetAsync(tokenUrl);
    var tokenJson = JsonDocument.Parse(await tokenResponse.Content.ReadAsStringAsync()).RootElement;
    var accessToken = tokenJson.GetProperty("access_token").GetString();

    var request = new HttpRequestMessage(HttpMethod.Get, "https://openapi.naver.com/v1/nid/me");
    request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    var userInfoResponse = await client.SendAsync(request);
    var userInfoJson = JsonDocument.Parse(await userInfoResponse.Content.ReadAsStringAsync()).RootElement;
    var response = userInfoJson.GetProperty("response");

    var naverId = response.GetProperty("id").GetString();
    var name = response.TryGetProperty("name", out var nameProp) ? nameProp.GetString() : null;
    var profileImage = response.TryGetProperty("profile_image", out var profileProp) ? profileProp.GetString() : null;

    var user = await _userService.FindByNaverIdAsync(naverId);
    if (user == null)
        user = await _userService.RegisterNaverUserAsync(naverId, name, profileImage);

    var jwt = _jwtService.GenerateToken(user);

    var html = $@"
    <html><body>
      <script>
        window.opener.postMessage({{ token: '{jwt}' }}, '{origin}');
        window.close();
      </script>
    </body></html>";

    return Results.Content(html, "text/html");
}

✅ 결론 및 실무 팁

  • 네이버 UID를 사용자 고유 키로 사용하여 중복 문제 방지
  • 기존 이메일 가입자와 소셜 로그인 사용자는 명확히 구분 (중복 가입 허용)
  • JWT 인증 방식으로 API 통합 유지
  • 운영 배포 시 로그인 UI 숨기고, 검수용 테스트만 노출하여 대응 가능

✅ 확장 고려

  • 카카오/구글 로그인 연동도 동일 패턴으로 구현 가능
  • DB에 Provider 필드를 추가하여 멀티 소셜 계정 지원 가능
  • 계정 통합 기능은 추후 수동 연동 절차로 구현
728x90

댓글