웹 서비스에서 잘 작동하던 소셜 로그인을 iOS나 Android 앱에 붙이려다 보면 생각지 못한 리다이렉트(Redirect) 문제에 직면하곤 합니다. 특히 클라이언트가 카카오/네이버 SDK를 직접 사용하는 것이 아니라, 백엔드에서 인증을 처리하고 클라이언트로 결과를 돌려주는 구조라면 더욱 그렇습니다.
오늘은 기존 웹 환경에 맞춰진 소셜 로그인 흐름을 앱(iOS) 환경에 맞게 개선하며 겪은 문제와 그 해결 과정을 정리해 보았습니다.
1. 기존 웹 기반 소셜 로그인 흐름과 문제점
일반적인 웹 환경에서의 소셜 로그인 흐름은 다음과 같습니다.
- 앱(클라이언트)이 "카카오 로그인 화면 열어줘"라고 백엔드에 요청합니다.
- 백엔드는 카카오 로그인 URL을 클라이언트에 반환합니다.
- iOS 앱이 해당 URL을 브라우저처럼 띄웁니다.
- 사용자가 카카오 로그인을 성공적으로 마칩니다.
- 카카오는 그 결과를 앱이 아닌 백엔드 콜백(Callback) 주소로 보냅니다.
- 백엔드는 카카오에서 받은 정보를 바탕으로 "기존 회원"인지, "신규 가입이 필요한 회원"인지 판단합니다.
- (문제 발생 구간) 백엔드가 최종 결과를 다시 앱(클라이언트)으로 돌려줍니다.
현재 시스템에서는 이 7번 과정에서 앱으로 결과를 돌려주는 것이 아니라, 아래와 같은 웹 프론트 주소로 리다이렉트하고 있었습니다.
- 기존 회원 (로그인 성공): https://myservice.com/login/success?loginCode=...
- 신규 회원 (가입 필요): https://myservice.com/signup/kakao?pendingToken=...
왜 앱에서는 문제가 될까?
웹 브라우저에서는 저 페이지로 이동한 뒤 프론트엔드가 URL에서 loginCode나 pendingToken을 파싱해 다음 처리를 하면 그만입니다.
하지만 iOS 앱은 저 웹 주소로 이동해 버리면 앱으로 제어권이 돌아오지 않습니다. 단순히 사파리(Safari)나 웹뷰에 머물게 되는 것이죠. 앱이 리다이렉트를 낚아채서 후속 처리를 할 수 있는 '앱 전용 주소'가 필요합니다.
2. 해결책: 커스텀 URL 스킴 (Custom URL Scheme) 도입
이 문제를 해결하기 위해서는 앱이 인식할 수 있는 고유한 주소, 즉 커스텀 URL 스킴(Custom URL Scheme)을 사용해야 합니다.
예를 들어 myservice://라는 스킴을 설정해 보겠습니다. iOS 앱에서 이 주소를 등록해두면, 브라우저가 저 주소로 이동하는 순간 OS가 "아, 이건 myservice 앱을 열어야 하는 주소구나!" 하고 앱으로 제어권을 다시 넘겨주게 됩니다.
따라서 백엔드는 소셜 로그인 콜백 처리가 끝난 후, 웹용 리다이렉트는 그대로 유지하되 클라이언트가 앱일 경우 앱용 주소로 보내주는 분기 처리를 추가해야 합니다.
3. 토큰 처리 구조 (loginCode vs pendingToken)
리다이렉트 주소를 넘겨줄 때 실제 인증에 사용되는 accessToken을 URL에 그대로 노출하는 것은 보안상 위험합니다. 따라서 우리 백엔드는 즉시 토큰을 주는 대신 임시 교환권 성격의 데이터를 내려줍니다.
유저의 가입 상태에 따라 두 가지로 나뉘어 동작합니다.
A. 기존 회원: loginCode
성공적으로 로그인된 기존 회원에게는 loginCode를 발급합니다.
리다이렉트 예시: myservice://auth/social/success?loginCode=abc
앱은 스킴을 통해 이 주소를 잡은 뒤, 추출한 loginCode를 이용해 백엔드의 토큰 교환 API를 호출합니다.
// POST /api/auth/social/login/exchange
{
"loginCode": "abc"
}
이 과정을 거쳐야만 비로소 실제 서비스에서 사용할 수 있는 진짜 로그인 토큰(accessToken 등)을 발급받아 로그인이 완료됩니다.
B. 신규 회원 및 계정 연동: pendingToken
아직 회원가입 완료 전이거나 기존 이메일과 연동이 필요한 경우 발급됩니다. "카카오 인증 정보는 백엔드에 잠깐 저장해 두었다"는 의미의 임시 키입니다.
리다이렉트 예시:
- 신규 회원 가입: myservice://auth/social/signup?provider=kakao&pendingToken=xyz
- 기존 계정 연동: myservice://auth/social/link?provider=kakao&pendingToken=xyz
앱은 이 pendingToken을 받은 후 추가 회원가입(또는 연동 확인) 프로세스를 진행하게 됩니다. 해당 토큰으로 백엔드에 저장된 임시 정보를 조회하고, 최종적으로 회원가입 완료 API를 호출하는 데 사용합니다.
4. 최종 요약 및 백엔드 작업 목표
결론적으로 웹용 리다이렉트는 그대로 살려두고, 클라이언트 환경에 따라 아래와 같은 앱용 리다이렉트 URI를 콜백 결과로 보내주어야 합니다.
- 기존 회원 로그인 성공
- myservice://auth/social/success?loginCode=...
- 신규 회원 추가 가입
- myservice://auth/social/signup?provider=kakao&pendingToken=...
- 기존 계정 연동 필요
- myservice://auth/social/link?provider=kakao&pendingToken=...
이 작업이 완료되면, iOS 앱은 웹 브라우저에서 인증이 끝난 직후 위 주소들을 낚아채어 유연하게 다음 동작(exchange API 호출 또는 회원가입 폼 이동)을 처리할 수 있게 됩니다.
'백엔드 > 🍃 SpringBoot' 카테고리의 다른 글
| boolean 필드 매핑 안될 때 (isTermsAgreed가 false만 나오는 이유) (0) | 2026.03.20 |
|---|---|
| [JPA] @OneToMany, @ManyToOne 초간단 정리 (0) | 2026.03.03 |
| 배포시 spring-boot-devtools 비활성화 (1) | 2025.06.13 |
| Spring Boot | 패키지 구조(계층형 vs 도메인형) (1) | 2025.05.29 |
| findAll()에서 Optional<List<T>>를 쓰지 않아도 되는 이유 (1) | 2025.05.20 |