시작하며
이번 코드잇 인턴에서 AWS Amplfiy Gen2를 기술스택으로 선정하였고, 나는 그중에서 User 관리 기능을 담당하게 되었다.
이 기능을 구현하면서 AWS Cognito를 적극적으로 활용했다. 그러나 기본 제공 기능만으로는 프로젝트 요구 사항을 완전히 충족하기 어려워, AWS SDK를 통해 보다 세밀한 User 관리 기능을 개발하게 되었다.
그래서 해당 구현 과정을 기록하기 위해 글을 작성하게 되었다.
구현한 기능
우선 해당 프로젝트는 사내에서 사용하는 서비스로 회원가입 기능은 없이 ADMIN이 멤버를 추가하고 수정하고 삭제하는 방식이다.
그래서 크게 다음과 같은 기능을 구현하였다.
- 멤버 추가
- 멤버 수정
- 멤버 삭제
- 로그인
- 비밀번호 재설정
- 비밀번호 변경
1. AWS SDK 사용 이유
Cognito의 기본 메서드만으로도 User 관련 기능은 충분히 구현할 수 있다.
하지만 처음에 멤버 추가 기능을 구현하려 할 때, 단순히 멤버를 추가하는 메서드가 제공되지 않아 가입 절차를 통해서만 사용자를 추가할 수 있다는 점을 알게 되었다.
Cognito의 기본 회원가입 절차는 사용자가 이메일 인증 코드를 통해 계정을 활성화해야 하는 방식인데, 해당 프로젝트는 사내 서비스로 이러한 과정이 불필요하게 복잡하다고 판단이 들었다.
그래서 이를 해결하고 개선하기 위해, AWS SDK를 사용해 Cognito에 직접 접근하는 방식을 선택하게 되었다.
2. AWS SDK 주의할 점
처음에 AWS SDK를 사용하기 위해 pnpm @aws-sdk
를 하여 설치한 후, Cognito 관련 메서드를 활용해 기능을 구현하였다.
그러나... 로컬 환경에서는 정상적으로 작동하던 코드가 배포 환경에서는 실행이 되지 않은 문제가 발생하였다.
해당 원인을 찾아보니, 각 서비스에 필요한 클라이언트 모듈을 정확하게 명시하여 별로도 설치해야 한다고 했다.
그래서 Cognito 클라이언트 모듈인 @aws-sdk/client-cognito-identity
를 설치하여 사용하였다.
3. key 발급
AWS SDK로 Cognito에 접근을 하기 위해서는 Cognito 권한이 있는 IAM의 엑세스 키와 비밀 엑세스 키가 필요하다.
발급받은 키로 Cognito와 통신할 클라이언트를 생성한다. 그러면 이 클라이언트를 통해 Cognito에 요청을 보내 사용자를 생성하거나 인증 관련 작업을 수행할 수 있다.
그리고 사용할 Cognito의 사용자 풀 ID도 필요하다.
4. 에러처리
4-1. AWS SDK의 Cognito 에러 클래스
AWS SDK의 Cognito에서 발생하는 에러를 처리하기 위해 CognitoIdentityProviderServiceException 를 활용했다.
에러 발생 에러 이름을 키로 삼아 메시지를 추출해 에러로 전달하고, 클라이언트 측에서는 이를 토스트로 표시해 사용자 경험을 개선했다.
4-2. AWS Amplfiy의 Auth 에러 클래스
에러가 AuthError의 인스턴스인 경우, 에러 이름에 맞는 메시지를 토스트를 통해 직관적으로 전달하도록 하였다.
기능 구현
1. 멤버 추가
[사용한 메서드]
AdminCreateUserCommand
AdminSetUserPasswordCommand
AdminAddUserToGroupCommand
1-1. 사용자 생성하기
사용자를 생성할 때는 이메일과 초기 임시 비밀번호를 설정한다.
이후 UserAttributes 배열에 이메일을 인증된 상태로 설정하고, MessageAction을 "SUPPRESS"로 지정하여 Cognito에서 기본으로 발송하는 환영 이메일을 차단한다.
1-2. 사용자 생성 요청 전송 및 응답 처리
cognitoClient.send(createUserCommand) 명령어로 사용자 생성 요청을 Cognito에 전송한다.
응답에 userId가 포함되지 않은 경우 에러를 발생시키며, userId가 포함된 경우 비밀번호를 영구적으로 설정한 후 userId를 반환한다.
참고로 비밀번호를 영구 설정한 이유는 사용자가 첫 로그인 시 비밀번호 변경 절차를 건너뛰기 위해서이다.
1-3. 비밀번호 영구 설정
Permanent 값을 true로 지정하여 사용자가 초기 비밀번호 변경 없이, 로그인할 수 있게 했다.
1-4. 사용자 그룹에 추가
또한 해당 서비스에는 ADMIN과 MEMBER 두 그룹이 존재하기 때문에, AdminAddUserToGroupCommand 메서드를 통해 사용자를 적절한 그룹에 추가했다.
1-5. DynamoDB에 데이터 추가
(1-2)에서 반환된 userId를 기반으로 DynamoDB에 유저 데이터를 추가하여 정상적으로 반영된 것을 확인할 수 있다.
2. 멤버 수정
[사용한 메서드]
AdminUpdateUserAttributesCommand
AdminRemoveUserFromGroupCommand
AdminAddUserToGroupCommand
2-1. 이메일 수정하기
AdminUpdateUserAttributesCommand 메서드를 활용하여 사용자 속성 중 이메일을 업데이트할 수 있다.
그래서 이 메서드를 통해 사용자의 이메일 속성을 새로운 이메일인 newEmail로 변경하고, 해당 이메일을 인증된 상태로 설정한다.
2-2. 그룹 삭제하기
Cognito에는 사용자가 소속된 그룹을 직접 변경하는 메서드가 제공되지 않으므로, 그룹으 먼저 삭제한 후 새로 그룹에 추가하는 방식으로 대체해야 한다.
그래서 AdminRemoveUserFromGroupCommand 메서드를 사용하여, 제거할 그룹명을 GroupName에 지정해 요청을 전송한다.
2-3. 그룹 추가하기
해당 과정은 (1-4)에서 사용한 함수와 동일하다.
2-4. 결과
Cognito에서 데이터를 수정한 후, DynamoDB에서도 사용자 정보를 업데이트하여 유저의 정보를 동기화하여 수정한다.
3. 멤버 삭제
사용자 삭제는 AdminDeleteUserCommand 메서드를 사용하여 간단히 수행할 수 있다.
4. 로그인
로그인 이후의 작업은 AWS SDK에 직접 접근하지 않고도, Cognito에서 제공하는 기본 메서드를 통해 구현할 수 있다.
특히 AWS Amplify Gen2부터는 "aws-amplify/auth" 모듈에서 필요한 메서드를 가져다 사용하며, 직관적인 기능들 덕분에 구현이 정말 간편하다.
4-1. signIn으로 로그인하기
signIn 메서드를 사용하면 이메일과 비밀번호로 간단히 로그인이 가능하며, 자동으로 토큰과 정보가 로컬스토리지에 저장된다.
그리고 로그인 과정에서 signInStep이 "CONFIRM_SIGN_IN_WITH_NEW_PASSWORD_REQUIRED" 상태일 경우, (1-3)에서 정의한 함수로 비밀번호를 강제 설정하였다.
왜냐하면 Cognito에 아무런 유저가 없는 상황에서 직접 유저를 추가했을 때, 비밀번호가 인증되지 않는 상황이기 때문에 이러한 상황에서 즉시 유저를 인증하기 위해 이 메서드를 호출하였다.
5. 비밀번호 재설정
비밀번호 재설정 기능 역시 Cognito의 기본 메서드로 간단하게 처리할 수 있다.
5-1. 비밀번호 재설정 요청
resetPassword 메서드를 사용해 비밀번호 재설정을 요청하면 이메일로 인증코드가 발송된다.
참고로 인증코드 메일의 템플릿은 "Cognito -> 메시징 -> 메시지 템플릿 -> 확인 메시지"에서 변경할 수 있다.
5-2. 비밀번호 재설정
이후 confirmResetPassword 메서드에 인증코드, 이메일, 새 비밀번호를 전달하여 비밀번호를 재설정한다.
6. 비밀번호 변경
비밀번호 변경은 내가 직접 구현하지는 않았으나, Cognito의 updatePassword 메서드로 손쉽게 구현할 수 있다.
마치며
인증 관련 작업을 백엔드로 직접 구현하는 과정은 매우 복잡하지만, AWS Amplify를 사용해 Cognito로 사용자 관련 기능을 간편하게 처리할 수 있었다.
특히 토큰 재발급과 같은 복잡한 과정도 Cognito에서 자동으로 관리해 주므로, 프론트엔드 개발자가 백엔드 구현 부담을 크게 줄일 수 있는 것 같다.
백엔드 구축 리소스를 절약할 수 있어, 프론트엔드 개발자가 백엔드까지 고려한 풀스택 서비스를 개발하고자 할 때 매우 유용한 것 같다.
'💜 프로젝트 구현' 카테고리의 다른 글
[AWS Amplfiy Gen2/Next.js] NextAuth.js 구글 로그인 구현기 (feat. Cognito) (0) | 2024.11.17 |
---|---|
[Jotai/Tailwind CSS] Toast 컴포넌트 직접 구현 (feat. Next.js App Router) (0) | 2024.09.19 |
[TanStack Query] 옵티미스틱 업데이트 사용하여 좋아요 구현 (1) | 2024.09.18 |
[Next.js] Lighthouse 웹 사이트 성능 개선 (100으로 만들기) (0) | 2024.09.10 |
[Next.js / TanStack query] useInfiniteQuery 사용하여 무한 스크롤 구현 (feat. cursor 방식) (0) | 2024.09.07 |
FE 개발자가 되고 싶은 짱잼이
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!