728x90
✅ 프로젝트 목표
기존 이메일/비밀번호 로그인 시스템이 있는 웹 애플리케이션에 네이버 소셜 로그인(OAuth2) 기능을 추가합니다. 로그인 후 네이버 계정을 통해 별도 회원으로 가입되며, JWT 방식으로 인증 처리됩니다.
✅ 전체 동작 시나리오
- 사용자가 "네이버 로그인" 버튼 클릭
- 팝업 창으로 네이버 로그인 화면으로 이동
- 로그인 성공 후, 등록된 redirect_uri로
code
와state
전달 - 백엔드에서 access_token 요청 → 사용자 정보 요청
- 네이버 UID로 사용자 조회 → 없으면 자동 회원가입
- JWT 발급 → 팝업창에서 부모창으로 전달
- 부모창은 토큰 저장 후 로그인 상태 처리
✅ 핵심 처리 방식
- 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
'React > React 실습' 카테고리의 다른 글
[React] Material-UI Popover 위치 보정과 애니메이션 적용 시행착오 기록 (0) | 2025.03.14 |
---|---|
[React] Material-UI Popover에서 Popper로 전환한 시행착오 (0) | 2025.03.14 |
[React] Drawer와 Tab, Accordian - Console 콘솔에러 제거하기 (0) | 2025.02.04 |
[React] Drawer와 Tab, Accordian으로 FAQ 구현하기 (0) | 2025.02.03 |
[React] KeyDown, KeyUp 한국어 2번? (0) | 2025.02.03 |
댓글