2026. 5. 31. 20:11ㆍ프로젝트/HOTSPOT
1) 오늘의 작업 요약
오늘 개발하면서 가장 헷갈렸던 부분은 OAuth 로그인 흐름이 프론트엔드와 백엔드, 그리고 배포 환경변수까지 동시에 맞아야 정상 동작한다는 점이었다. 단순히 구글/카카오 버튼을 추가하는 작업처럼 보였지만, 실제로는 인증 요청을 어디서 시작하고, 콜백을 어디서 처리하며, 컨테이너 내부에서 어떤 환경변수를 읽는지까지 함께 맞춰야 했다.
나는 오늘 HOTSPOT의 소셜 로그인 기능을 중심으로 작업했다. 백엔드에는 Google/Kakao OAuth 로그인 처리를 추가했고, 프론트엔드에는 소셜 로그인 UI와 OAuth 완료 페이지 정리를 반영했다. 또한 서비스 전반의 브랜드명을 HOTSPOT으로 통일하고, 로그인 화면의 뒤로가기 UX를 개선했다. 판매자 사기 방지를 위한 1단계 작업으로 사업자등록번호 입력과 어드민 승인/거절 UI도 추가했다.
- Google/Kakao OAuth 소셜 로그인 기능 추가
- 백엔드 컨테이너에 OAuth 관련 환경변수 전달
- 프론트엔드 로그인 화면에 소셜 로그인 UI 추가
- OAuth 완료 페이지의 브랜드명 HOTSPOT 통일 및 닉네임 pre-fill 제거
- 전체 로고/브랜드명을 HOTSPOT으로 통일
- 로그인 화면 뒤로가기 버튼 추가
- 사업자등록번호 입력 및 어드민 승인/거절 UI 추가
2) 주요 변경사항 상세
2-1. Google/Kakao OAuth 소셜 로그인 추가
OAuth는 사용자가 우리 서비스에 직접 비밀번호를 입력하지 않고, 구글이나 카카오 같은 외부 인증 제공자를 통해 로그인할 수 있게 해주는 방식이다. 핵심 흐름은 사용자를 인증 제공자 페이지로 이동시키고, 인증이 끝난 뒤 전달받은 인가 코드를 백엔드가 토큰으로 교환하는 구조다.
나는 백엔드에서 Google과 Kakao 로그인 처리를 추가하면서, provider별로 필요한 client id, client secret, redirect uri를 환경변수로 분리했다. 이렇게 한 이유는 개발/운영 환경마다 OAuth 앱 설정이 달라질 수 있고, 민감한 값이 코드에 직접 들어가면 보안상 위험하기 때문이다.
@GetMapping("/oauth2/{provider}/login")
public ResponseEntity<OAuthLoginResponse> login(
@PathVariable String provider,
@RequestParam String code
) {
OAuthLoginResponse response = oauthService.login(provider, code);
return ResponseEntity.ok(response);
}
이 구조에서는 프론트엔드가 provider와 authorization code를 백엔드에 전달하고, 백엔드는 provider에 맞는 OAuth 클라이언트를 선택해 사용자 정보를 조회한다. 이후 HOTSPOT 회원 정보와 연결하거나 신규 회원 가입 흐름으로 넘길 수 있다.
2-2. OAuth 환경변수를 백엔드 컨테이너에 전달
트러블슈팅 흐름은 비교적 명확했다.
- 문제: 로컬에서는 OAuth 설정을 읽을 수 있는데, 백엔드 컨테이너에서는 OAuth 로그인이 실패했다.
- 원인: 컨테이너 실행 환경에 Google/Kakao OAuth 관련 환경변수가 전달되지 않았다.
- 해결: 백엔드 컨테이너 설정에 필요한 OAuth 환경변수를 명시적으로 추가했다.
backend:
environment:
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET}
KAKAO_CLIENT_ID: ${KAKAO_CLIENT_ID}
KAKAO_CLIENT_SECRET: ${KAKAO_CLIENT_SECRET}
이번 작업을 통해 다시 느낀 점은, Spring 애플리케이션에서 환경변수는 애플리케이션 내부 설정만의 문제가 아니라 실행 환경의 문제이기도 하다는 것이다. 코드에서 @Value나 configuration properties로 값을 읽도록 작성해도, 컨테이너가 그 값을 받지 못하면 결국 런타임에서는 빈 값이 된다.
2-3. 프론트엔드 소셜 로그인 UI 추가
프론트엔드에는 구글/카카오 로그인 버튼을 추가했다. 사용자가 버튼을 누르면 각 provider의 OAuth 인증 URL로 이동하고, 인증 완료 후 redirect uri를 통해 다시 HOTSPOT으로 돌아오는 방식이다.
const handleOAuthLogin = (provider) => {
window.location.href = `/oauth2/authorization/${provider}`;
};
<button onClick={() => handleOAuthLogin('google')}>
Google로 로그인
</button>
<button onClick={() => handleOAuthLogin('kakao')}>
Kakao로 로그인
</button>
OAuth 로그인 UI는 일반 로그인과 같은 화면에 배치했다. 사용자가 로그인 방식을 선택할 때 화면을 이동하지 않아도 되도록 하고, 기존 로그인 흐름과 소셜 로그인 흐름이 자연스럽게 공존하도록 만들기 위해서다.
2-4. OAuth 완료 페이지 정리
OAuthCompletePage에서는 브랜드명을 HOTSPOT으로 통일하고, 닉네임 pre-fill 동작을 제거했다. 외부 OAuth 제공자에서 내려주는 이름이나 닉네임을 그대로 입력값에 채우면 편리할 수 있지만, 사용자가 HOTSPOT에서 사용할 닉네임을 직접 선택하는 흐름이 더 명확하다고 판단했다.
특히 닉네임은 서비스 내에서 사용자 정체성을 나타내는 값이므로, 외부 계정의 프로필 값을 무조건 가져오기보다 사용자가 확인하고 입력하는 편이 UX와 데이터 품질 측면에서 더 낫다고 느꼈다.
2-5. 브랜드명 HOTSPOT 통일
백엔드와 프론트엔드 전반에서 로고와 브랜드명을 HOTSPOT으로 통일했다. 서비스 이름이 여러 형태로 섞이면 사용자는 같은 서비스인지 헷갈릴 수 있고, 개발 중에도 화면별 문구 기준이 흐려진다.
그래서 로그인, OAuth 완료 페이지, 공통 로고 영역 등 사용자에게 직접 노출되는 부분을 HOTSPOT 기준으로 맞췄다. 작은 변경처럼 보이지만, 서비스의 일관성을 만드는 데 중요한 작업이었다.
2-6. 로그인 화면 뒤로가기 버튼 추가
로그인 화면에는 뒤로가기 버튼을 추가했다. 이때 무조건 브라우저 뒤로가기를 실행하면 사용자가 직접 URL로 로그인 페이지에 들어온 경우 이동할 이전 페이지가 없을 수 있다. 그래서 히스토리가 있으면 뒤로 이동하고, 없으면 홈으로 이동하는 방식으로 처리했다.
const handleBack = () => {
if (window.history.length > 1) {
navigate(-1);
} else {
navigate('/');
}
};
이 방식은 사용자의 진입 경로를 고려한 처리다. 앱 내부에서 로그인 페이지로 온 사용자는 원래 보던 화면으로 돌아갈 수 있고, 외부에서 바로 들어온 사용자는 빈 히스토리 대신 홈으로 이동할 수 있다.
2-7. 사업자등록번호 입력 및 어드민 승인/거절 UI
판매자 사기 방지를 위한 1단계 작업으로 사업자등록번호 입력과 어드민 승인/거절 UI를 추가했다. 아직 완전한 검증 자동화까지는 아니더라도, 판매자 등록 과정에 승인 단계를 두면 무분별한 판매자 진입을 줄일 수 있다.
이 기능의 핵심은 판매자가 사업자 정보를 제출하고, 관리자가 이를 확인한 뒤 승인 또는 거절 상태로 변경하는 것이다. 프론트엔드에서는 판매자 입력 화면과 관리자 검토 UI를 구분해 구성했다.
const handleApprove = (sellerId) => {
updateSellerStatus(sellerId, 'APPROVED');
};
const handleReject = (sellerId) => {
updateSellerStatus(sellerId, 'REJECTED');
};
나는 이 작업을 하면서 판매자 등록은 단순 회원가입과 다르게 신뢰 검증 흐름이 필요하다는 점을 다시 정리했다. 특히 HOTSPOT처럼 거래나 예약과 연결될 수 있는 서비스에서는 판매자 상태값을 명확하게 관리하는 것이 중요하다.
3) 기술 메모/배운 점
Q. OAuth와 일반 로그인은 무엇이 다른가?
A. 일반 로그인은 사용자가 HOTSPOT에 직접 아이디와 비밀번호를 입력하고, HOTSPOT 서버가 그 정보를 검증한다. 반면 OAuth 로그인은 구글이나 카카오 같은 외부 인증 제공자가 사용자를 인증하고, HOTSPOT은 그 결과로 사용자 정보를 받아 로그인 처리를 한다.
- 일반 로그인: 우리 서비스가 인증 정보를 직접 관리한다.
- OAuth 로그인: 외부 provider가 인증을 담당하고, 우리 서비스는 인가 결과를 받아 처리한다.
Q. OAuth에서 redirect uri가 중요한 이유는?
A. redirect uri는 인증 제공자가 인증 완료 후 사용자를 다시 돌려보낼 주소다. 이 값이 OAuth 앱 설정과 실제 요청 값에서 일치하지 않으면 인증 제공자는 보안상 요청을 거절한다. 따라서 개발 환경, 운영 환경, 컨테이너 환경에서 redirect uri를 정확히 맞추는 것이 중요하다.
Q. 환경변수를 코드에 직접 쓰지 않는 이유는?
A. OAuth client secret 같은 값은 민감 정보다. 코드에 직접 넣으면 Git에 노출될 수 있고, 환경별 설정 변경도 어려워진다. 환경변수로 분리하면 같은 코드라도 개발, 테스트, 운영 환경에서 다른 설정을 주입할 수 있다.
Q. 닉네임 pre-fill을 제거한 이유는?
A. OAuth provider에서 받은 이름은 사용자의 실제 서비스 닉네임과 다를 수 있다. 자동으로 채우면 편리하지만, 사용자가 의도하지 않은 이름으로 가입할 가능성도 있다. 그래서 HOTSPOT에서 사용할 닉네임은 사용자가 직접 입력하도록 흐름을 정리했다.
정리
오늘 작업은 단순히 로그인 버튼을 추가한 것이 아니라, HOTSPOT의 인증 흐름과 브랜드 일관성, 판매자 신뢰 검증의 기반을 함께 다진 작업이었다. OAuth는 프론트엔드 화면, 백엔드 인증 처리, 외부 provider 설정, 컨테이너 환경변수가 모두 맞아야 완성되는 기능이라는 점을 다시 확인했다. 다음 작업에서는 OAuth 예외 처리와 판매자 승인 상태에 따른 접근 제어를 더 촘촘하게 다듬으면 좋겠다고 느꼈다.
'프로젝트 > HOTSPOT' 카테고리의 다른 글
| [HOTSPOT] 2026.06.03 - 주요 작업 요약 (0) | 2026.06.03 |
|---|---|
| [HOTSPOT] 2026.06.02 - 주요 작업 요약 (0) | 2026.06.02 |
| [HOTSPOT] 2026.06.01 - 주요 작업 요약 (0) | 2026.06.01 |
| [HOTSPOT] 2026.05.30 - 주요 작업 요약 (0) | 2026.05.30 |
| [HOTSPOT] 2026.05.29 - 주요 작업 요약 (1) | 2026.05.29 |