1. 개요
| 항목 | 내용 |
|---|---|
| 발생 시점 | AWS Cognito User Pool을 기존 프로젝트 회원가입 기능에 적용하는 과정 |
| 환경 | AWS Cognito User Pool, WAS EC2, FastAPI WAS, MariaDB users 테이블 |
| 대상 기능 | 회원가입 및 로그인 인증 기능 |
| 영향 | 회원가입 실패 후 재가입 시 “이미 존재하는 사용자” 오류 발생 |
| 주요 원인 | Cognito 사용자 생성 후 비밀번호 설정 단계에서 실패했지만, 생성된 Cognito 사용자가 정리되지 않음 |
| 조치 | 이메일 기반 username 처리, Cognito 사용자 속성 전달, 실패 시 Cognito 사용자 삭제 로직 추가 |
기존 서비스는 내부 DB의 users 테이블을 기준으로 일반 문자열 아이디를 사용해 회원가입을 처리하고 있었다.
그러나 AWS Cognito User Pool을 적용하면서 기존 로컬 회원가입 방식과 Cognito의 사용자 생성 방식 차이로 문제가 발생했다.
기존에 생성되어 있던 Cognito User Pool은 설정상 이메일 형식의 username을 요구했다. 또한 Cognito 사용자 생성은 성공했지만, 비밀번호 영구 설정 단계에서 비밀번호 정책 오류가 발생하면서 Cognito에는 FORCE_CHANGE_PASSWORD 상태의 사용자가 남는 상황이 발생했다.
이로 인해 사용자는 회원가입에 실패했다고 인식하지만, Cognito에는 이미 사용자가 남아 있어 이후 동일 이메일로 재가입할 때 “이미 존재하는 사용자” 오류가 발생했다.
2. 기존 회원가입 구조
기존 회원가입 방식은 WAS와 DB 중심으로 동작했다.
이 구조에서는 사용자 계정의 기준 저장소가 내부 DB였다.
따라서 회원가입 실패 여부도 대부분 WAS 로직 또는 DB 저장 결과를 기준으로 판단할 수 있었다.
3. Cognito 적용 후 회원가입 구조
Cognito User Pool 적용 후에는 외부 인증 저장소인 Cognito와 내부 서비스 DB가 함께 사용되었다.
이 구조에서는 사용자가 두 곳에 저장된다.
따라서 Cognito 사용자 생성, 비밀번호 설정, DB 저장 중 어느 단계에서 실패하더라도 사용자 상태가 불일치할 수 있다.
4. 증상
회원가입 과정에서 다음 문제가 발생했다.
대표적인 증상은 다음과 같다.
이 문제는 단순한 프론트엔드 오류가 아니라, Cognito와 내부 DB 간 사용자 상태 불일치 문제였다.
5. 원인 분석
5.1 기존 로컬 로그인 방식과 Cognito 사용자 모델 차이
기존 서비스는 일반 문자열 아이디를 기준으로 회원가입을 처리했다.
예시:
반면 기존에 생성되어 있던 Cognito User Pool은 설정상 이메일 형식의 username을 요구했다.
예시:
따라서 기존 회원가입 입력값을 그대로 Cognito 사용자 생성 요청에 사용하면 User Pool 설정과 맞지 않는 문제가 발생할 수 있었다.
5.2 Cognito 사용자 속성 누락
Cognito 사용자 생성 시 단순히 username만 전달하는 것이 아니라, User Pool 설정에 따라 email, email_verified 같은 사용자 속성을 함께 전달해야 했다.
수정 전에는 Cognito에서 기대하는 사용자 속성이 충분히 전달되지 않아, 회원가입 또는 이후 인증 흐름에서 문제가 발생할 가능성이 있었다.
수정 후에는 Cognito 사용자 생성 시 다음 속성을 함께 전달하도록 변경했다.
개념적으로는 다음과 같다.
이를 통해 프론트엔드에서 입력받은 이메일과 Cognito User Pool의 username 기준을 일치시켰다.
5.3 Cognito 사용자 생성과 후속 처리는 자동 롤백되지 않음
Cognito는 외부 관리형 인증 서비스이고, 내부 DB는 별도의 서비스 저장소다.
또한 Cognito 내부에서도 다음 작업은 별도 API 호출로 처리된다.
이번 사례에서는 admin_create_user는 성공했지만, admin_set_user_password 단계에서 비밀번호 정책 오류가 발생했다.
문제는 admin_set_user_password가 실패해도 앞에서 생성된 Cognito 사용자가 자동으로 삭제되지 않는다는 점이었다.
즉, 다음과 같은 상태가 발생했다.
이로 인해 사용자는 회원가입에 실패했지만, Cognito 기준으로는 이미 존재하는 사용자로 남게 되었다.
6. 해결 과정
6.1 Cognito User Pool 설정 확인
먼저 기존에 생성되어 있던 Cognito User Pool 설정을 확인했다.
확인한 주요 항목은 다음과 같다.
확인 결과, 해당 User Pool 설정에서는 이메일 형식의 username을 사용해야 하는 구조였다.
6.2 WAS 회원가입 로직 수정
WAS 회원가입 로직에서 Cognito 사용자 생성 시 이메일 기반 username을 사용하도록 수정했다.
또한 Cognito 사용자 속성으로 email, email_verified를 함께 전달하도록 변경했다.
6.3 프론트엔드 입력값 검증 추가
기존에는 일반 문자열 아이디 입력을 전제로 했지만, Cognito 적용 후에는 이메일 형식 입력이 필요했다.
따라서 프론트엔드에서도 사용자에게 이메일 형식 입력을 안내하고, 잘못된 형식의 값은 사전에 검증하도록 변경했다.
검증 기준은 다음과 같다.
6.4 Cognito 사용자 정리 로직 추가
가장 중요한 수정은 Cognito 사용자 생성 후 후속 처리에 실패했을 때, 이미 생성된 Cognito 사용자를 자동 삭제하는 예외 처리 로직을 추가한 것이다.
문제 상황은 다음과 같다.
이를 방지하기 위해 다음 흐름으로 수정했다.
수정된 핵심 흐름은 다음과 같다.
삭제 로직은 다음과 같이 분리했다.
여기서 중요한 점은 사용자가 입력한 이메일이 아니라, Cognito 응답에서 받은 내부 Username 값을 삭제 대상으로 사용했다는 것이다.
실제 Cognito에서는 이메일을 username으로 전달해도, User Pool 설정에 따라 내부 Username이 UUID 형태로 생성될 수 있기 때문이다.
6.5 예외 응답 정리
Cognito 예외를 그대로 노출하지 않고, API 응답에 맞게 변환했다.
이를 통해 비밀번호 정책 실패는 400 Bad Request로 처리하고, 이미 존재하는 사용자는 409 Conflict로 처리하도록 정리했다.
6.6 테스트 코드 작성
회원가입 실패 상황에서 Cognito 사용자가 정상적으로 정리되는지 테스트 코드를 작성했다.
검증한 항목은 다음과 같다.
테스트 예시는 다음과 같다.
6.7 PR 반영 및 재배포
수정된 코드는 GitHub PR로 반영했다.
이후 배포 절차는 다음과 같이 진행했다.
7. 검증 방법
7.1 WAS 로그 확인
회원가입 실패 원인을 확인하기 위해 WAS 로그를 확인한다.
확인할 항목은 다음과 같다.
7.2 Cognito User Pool 사용자 상태 확인
AWS Console 또는 AWS CLI에서 Cognito User Pool에 사용자가 남아 있는지 확인한다.
회원가입 실패 후에도 사용자가 남아 있다면 Cognito와 DB 간 상태 불일치가 발생한 것이다.
출력 예시는 다음과 같다.
필요 시 다음 명령으로 수동 삭제할 수 있다.
예:
7.3 DB users 테이블 확인
DB에 사용자가 저장되었는지 확인한다.
현재 서비스에서는 이메일 값을 users.username 컬럼에 저장하고 있다.
정상적인 회원가입 완료 상태는 다음과 같다.
비정상 상태는 다음과 같다.
7.4 재가입 테스트
동일 이메일로 재가입을 다시 시도하여 사용자 상태 정리 여부를 확인한다.
정상 기준은 다음과 같다.
8. 트러블슈팅 흐름
9. 재발 방지
운영 배포에서는 다음 원칙을 적용한다.
- Cognito User Pool 설정을 먼저 확인한 뒤, 서비스 회원가입 모델과 맞춘다.
- username, email, user attribute 기준을 명확히 정한다.
- 기존 로컬 로그인 방식의 username 정책을 Cognito에 그대로 적용하지 않는다.
- Cognito 사용자 생성과 후속 처리는 자동 롤백되지 않으므로, 실패 시 보상 작업을 구현한다.
- Cognito 사용자 생성 후 비밀번호 설정 또는 DB 저장이 실패하면 Cognito 사용자를 삭제한다.
- 프론트엔드에서도 이메일 형식과 비밀번호 정책을 사전에 검증한다.
- 배포 후에는 프론트엔드 화면만 보지 않고 WAS 로그, Cognito 사용자 상태, DB users 테이블을 함께 확인한다.
- 테스트 코드에 외부 인증 서비스 연동 실패 및 정리 로직을 포함한다.
- 운영 문서에는 실제 이메일, 사용자 ID, 토큰, 비밀번호를 남기지 않는다.
10. 배운 점
이번 경험을 통해 인증 기능은 단순히 로그인 API를 연결하는 작업이 아니라, 외부 인증 서비스의 정책과 기존 서비스의 사용자 모델을 정확히 맞추는 작업이라는 점을 확인했다.
특히 Cognito 같은 관리형 인증 서비스를 사용할 때는 다음 요소를 함께 고려해야 한다.
또한 배포 후 문제가 발생했을 때 프론트엔드 오류 메시지만 보고 판단하면 원인을 놓칠 수 있다.
이번 문제는 WAS 로그, Cognito 사용자 상태, DB users 테이블을 함께 확인하면서 원인을 파악할 수 있었다.
이를 통해 운영 환경에서는 코드 수정뿐 아니라 인프라 설정, 권한, 외부 서비스 상태, 내부 데이터 상태를 함께 점검하는 습관이 중요하다는 점을 배웠다.
11. 직무 적용점
실무에서 클라우드 기반 서비스를 운영할 때는 기능 구현뿐 아니라 장애 발생 시 원인을 계층별로 분리해 확인하는 역량이 중요하다.
이번 프로젝트에서는 인증 요청 경로를 기준으로 다음 요소를 순서대로 확인했다.
이 경험을 바탕으로 실제 기업 환경에서도 인증, 배포, 네트워크, 권한, 데이터베이스 문제를 구조적으로 분석하고 개선할 수 있도록 적용하겠다.
특히 외부 관리형 서비스를 사용할 때는 서비스 자체의 정책과 내부 애플리케이션 모델이 다를 수 있으므로, 연동 전 설정값과 데이터 흐름을 먼저 확인하는 방식으로 문제를 예방하겠다.
12. 결론
이번 문제의 본질은 Cognito 설정 오류 하나가 아니라, 외부 인증 저장소와 내부 DB 간 사용자 상태 불일치였다.
기존 서비스는 내부 DB 중심의 로컬 회원가입 구조였지만, Cognito 적용 후에는 Cognito User Pool과 DB users 테이블을 함께 고려해야 했다.
특히 이번 사례에서는 Cognito 사용자 생성 이후 비밀번호 영구 설정 단계에서 실패했지만, 이미 생성된 Cognito 사용자가 자동 삭제되지 않아 FORCE_CHANGE_PASSWORD 상태로 남았다.
현재는 이메일 기반 username 처리, Cognito 사용자 속성 전달, 실패 시 Cognito 사용자 삭제 로직, 관련 테스트 코드까지 반영했으므로 동일한 유형의 재가입 오류를 방지할 수 있다.