Single-Spa Parcel 구성
이 애플리케이션은 Amazon Cognito를 이용해 사용자 회원가입, 로그인, 로그아웃 등의 인증 기능을 제공합니다.
회원가입 시 이메일 인증을 통해 사용자를 확인하며, 로그인 후에는 액세스 토큰, 리프레시 토큰, ID 토큰을 로컬 스토리지에 저장하여 인증 상태를 유지합니다.
안전하고 신뢰할 수 있는 사용자 인증 흐름을 제공합니다.




웹 및 모바일 앱을 위한 자격 증명 플랫폼
사용자 디렉터리, 인증 서버, OAuth 2.0 액세스 토큰 및 자격 증명에 대한 권한 부여 서비스
Cognito 사용자 풀 옵션
- 사용자 풀 로그인 옵션: 사용자 이름 (아이디)
- 가입 필수 옵션: 사용자 이름, 이메일
환경변수
VITE_COGNITO_USER_POOL_ID= # Cognito 사용자 풀 아이디
VITE_COGNITO_CLIENT_ID= # Cognito 앱클라이언트 아이디


- idToken; 클라이언트에서 사용자 정보를 가져올 때 사용
- accessToken; 백엔드 서비스에 접근할 때 사용
- refreshToken; 두 토큰의 만료 시 갱신에 사용
테스트 통과 여부와 커버리지 현황은 시각적으로 제공되며, 매 릴리즈 시 자동으로 최신 상태로 반영됩니다.
커버리지는 Codecov를 통해 분석됩니다.
테스트 리포트 바로가기 | 커버리지 대시보드 바로가기 |
프로젝트에서 사용되는 타입 정의를 문서화한 자료입니다.
이 타입 문서는 매 릴리즈 업데이트 시 자동으로 최신 상태로 배포됩니다.
타입 문서 바로가기 |
sequenceDiagram
actor User
participant Frontend
participant Cognito
participant API Gateway
participant Backend
User ->> Frontend: 로그인 정보 입력
Frontend ->> Cognito: 인증 요청 (username, password)
Cognito -->> Frontend: 토큰(ID / Access / Refresh)
note over Frontend: 토큰(ID / Access / Refresh)은 localStorage에 저장됨
Frontend ->> Frontend: ID Token을 디코딩해 사용자 정보 추출
alt Access Token 만료됨
alt Refresh Token 유효
Frontend ->> Cognito: 새 Access Token 요청 (Refresh Token)
Cognito -->> Frontend: 새 Access Token 응답
Frontend ->> API Gateway: API 요청
else Refresh Token도 만료됨
Frontend -->> User: 재로그인 요청
end
else Access Token 유효
loop API 요청 반복
Frontend ->> API Gateway: API 요청<br>Authorization: Bearer <ID Token><br>X-Access-Token: Bearer <Access Token>
note right of Frontend: <ID Token>은 인증용, <Access Token>은 인가용으로 전송
API Gateway ->> Cognito: ID Token 검증 (User Pool Authorizer)
Cognito -->> API Gateway: 검증 결과 (Claim 포함)
API Gateway ->> Backend: API 요청 전달<br>Authorization: Bearer <Access Token>
Backend ->> Backend: Access Token 디코딩 및 권한 확인
Backend -->> API Gateway: 응답 데이터
API Gateway -->> Frontend: 응답 데이터
end
end
- 프론트엔드는 Cognito SDK를 사용해 사용자 인증을 자체적으로 처리하고, 응답으로 받은 토큰을 저장한다.
- 프론트엔드는 ID Token을 디코딩하여 사용자 정보를 활용한다.
- Access Token이 만료되면 Refresh Token으로 갱신하고, Refresh Token까지 만료되면 재로그인이 필요하다.
- API 요청을 하며 두 토큰을 하나의 요청에 각각 다른 헤더에 담아 전송된다.
Authorization: Bearer <ID Token> X-Access-Token: Bearer <Access Token>
- API Gateway는 Cognito User Pool Authorizer를 통해 ID Token으로 사용자를 인증(authentication)한다.
- API Gateway는 X-Access-Token을 Authorization 헤더로 덮어써서 백엔드로 전달한다.
- 백엔드는 전달받은 Access Token을 디코딩되어 인가(authorization) 처리를 담당한다.
graph LR
subgraph CD[🚀 CD 영역]
direction LR
Tag[태그 푸시] --> DeployGH[gh-pages에 배포] --> |자동 워크플로 실행|pages-build-deployment[GitHub Pages 배포 완료]
Tag --> DeployAWS[Amazon S3에 배포] --> |콘텐츠 서빙|CloudFront[Amazon CloudFront]
Tag --> Codecov[CodeCov에<br>테스트 커버리지 배포]
end
Build -.-> |📦 아티팩트|Tag
subgraph CI[🧪 CI 영역]
direction LR
Push[브랜치 푸시] --> Lint[린트]
Lint --> |🟢 통과|Test[테스트]
Test --> |🟢 통과|Docs[문서화] --> Review
Test --> |🟢 통과|Build[빌드]
Build --> |🟢 통과|Review[리뷰]
Review -->|✔️ 승인|Merge[머지]
end
click Build "https://github.com/Daily1Hour/PickMe-Auth-Parcel/actions/workflows/vite-build.yml"
click Review "https://github.com/Daily1Hour/PickMe-Auth-Parcel/actions/workflows/auto-assign.yml"
click DeployGH "https://github.com/Daily1Hour/PickMe-Auth-Parcel/actions/workflows/deploy-gh-pages.yml"
click pages-build-deployment "https://github.com/Daily1Hour/PickMe-Auth-Parcel/actions/workflows/pages/pages-build-deployment"
click DeployAWS "https://github.com/Daily1Hour/PickMe-Auth-Parcel/actions/workflows/deploy-aws-s3.yml"
click Codecov "https://github.com/Daily1Hour/PickMe-Auth-Parcel/actions/workflows/deploy-codecov.yml"
열기
PickMe-Auth-Parcel
├─ src
│ ├─ main.tsx # 개발 서버 진입점
│ ├─ parcel.tsx # single-spa Parcel 빌드 진입점
│ ├─ app
│ │ └─ App.tsx # 프로바이더 스택
│ ├─ entities # 도메인 모델
│ │ └─ auth
│ │ ├─ index.ts
│ │ ├─ api
│ │ │ └─ dto.ts # dto 모델
│ │ ├─ config
│ │ │ └─ userPool.ts # Cognito 유저풀 정보 및 인스턴스
│ │ ├─ model # 모델 및 유효성 검사
│ │ │ ├─ index.ts
│ │ │ ├─ LoginCredential.ts
│ │ │ └─ SignupCredential.ts
│ │ ├─ repository # 브라우저 데이터 접근
│ │ │ └─ getLoggedIn.ts
│ │ └─ service # 유즈케이스
│ │ ├─ index.ts
│ │ ├─ login # 로그인
│ │ │ ├─ login.ts
│ │ │ │ ├─ login.test.ts
│ │ │ │ └─ login.usage.ts
│ │ │ ├─ forgotPassword.ts
│ │ │ │ ├─ forgotPassword.test.ts
│ │ │ │ └─ forgotPassword.usage.ts
│ │ │ └─ resetPassword.ts
│ │ ├─ session # 토큰 사용
│ │ │ ├─ getTokens.ts
│ │ │ │ ├─ getTokens.test.ts
│ │ │ │ └─ getTokens.usage.ts
│ │ │ └─ getUser.ts
│ │ └─ signup # 회원가입
│ │ ├─ signup.ts
│ │ │ ├─ signup.test.ts
│ │ │ └─ signup.usage.ts
│ │ └─ confirm.test.ts
│ │ └─ confirm.ts
│ ├─ features # 기능 구현체
│ │ ├─ authActions # 로그인/회원가입 기능
│ │ │ ├─ index.ts
│ │ │ ├─ api # 쿼리
│ │ │ │ ├─ index.ts
│ │ │ │ ├─ useLoginFetch.ts
│ │ │ │ ├─ useForgotPasswordFetch.ts
│ │ │ │ ├─ useResetPasswordFetch.ts
│ │ │ │ ├─ useSignupFetch.ts
│ │ │ │ └─ useConfirmFetch.ts
│ │ │ ├─ atom # 상태저장소
│ │ │ │ ├─ index.ts
│ │ │ │ └─ actionTypeAtom.ts
│ │ │ ├─ hook # 폼 커스텀훅
│ │ │ │ ├─ index.ts
│ │ │ │ ├─ useLoginForm.ts
│ │ │ │ ├─ useForgotPasswordForm.ts
│ │ │ │ ├─ useResetPasswordForm.ts
│ │ │ │ ├─ useSignupForm.ts
│ │ │ │ └─ useConfirmForm.ts
│ │ │ ├─ model # 스키마
│ │ │ │ ├─ index.ts
│ │ │ │ ├─ LoginSchema.ts
│ │ │ │ ├─ ForgotPasswordSchema.ts
│ │ │ │ ├─ ResetPasswordSchema.ts
│ │ │ │ ├─ SignupSchema.ts
│ │ │ │ └─ ConfirmSchema.ts
│ │ │ └─ ui
│ │ │ ├─ index.ts
│ │ │ ├─ forms
│ │ │ │ ├─ Field.tsx # 필드
│ │ │ │ ├─ Layout.tsx # 폼 레이아웃
│ │ │ │ ├─ LoginForm.tsx # 로그인 폼
│ │ │ │ ├─ ForgotPasswordForm.tsx # 비밀번호 찾기 폼
│ │ │ │ ├─ ResetPasswordForm.tsx # 비밀번호 리셋 폼
│ │ │ │ ├─ SocialLoginForm.tsx # 소셜로그인 폼
│ │ │ │ ├─ SignupForm.tsx # 회원가입 폼
│ │ │ │ └─ ConfirmForm.tsx # 회원가입 인증 폼
│ │ │ ├─ ActionLayout.tsx # 액션 레이아웃
│ │ │ └─ PopoverLayout.tsx # 팝오버 레이아웃
│ │ └─ userMenu # 로그인 인증 후 사용자메뉴 기능
│ │ ├─ index.ts
│ │ ├─ api
│ │ │ ├─ index.ts
│ │ │ ├─ useLoggedIn.ts
│ │ │ └─ useUserInfo.ts
│ │ └─ ui
│ │ └─ UserMenu.tsx
│ ├─ pages # 페이지
│ │ └─ auth
│ │ ├─ index.tsx
│ │ ├─ hook
│ │ │ └─ useTokens.ts
│ │ └─ ui
│ │ ├─ index.ts
│ │ ├─ AuthControls.tsx # 로그인/회원가입 컨트롤
│ │ └─ TokenInfo.tsx # 로그인 후 토큰 정보
│ ├─ shared # 공용
│ │ ├─ ActionType.ts
│ │ ├─ theme.ts
│ │ ├─ trans-ko.ts
│ │ ├─ styles
│ │ │ ├─ global.css
│ │ │ └─ index.js
│ │ └─ ui
│ │ ├─ atoms
│ │ │ ├─ index.ts
│ │ │ ├─ ButtonBackground.tsx
│ │ │ └─ StyledButton.tsx
│ │ ├─ index.ts
│ │ └─ molecules
│ │ ├─ index.ts
│ │ ├─ CircleButton.tsx
│ │ ├─ PrimaryButton.tsx
│ │ └─ SecondaryButton.tsx
│ ├─ third-party
│ │ └─ chakra-ui
│ └─ userscript # 유저스크립트
│ ├─ widget.meta.ts # 메타데이터
│ └─ widget.user.js # 스크립트
├─ tsconfig.json # ts 설정
│ ├─ tsconfig.app.json
│ ├─ tsconfig.node.json
│ └─ typedoc.json # 문서화 설정
├─ package.json # 의존성 설정
│ ├─ .prettierrc # 포맷터 설정
│ ├─ eslint.config.js # 린트 설정
│ └─ steiger.config.ts # FSD 린트 설정
└─ vite.config.ts # Vite 설정 파일
└─ vite-env.d.ts # 환경변수 타입 정의
$ npm install
$ npm run dev
- 빌드
- 배포 또는 프리뷰
- 배포 주소를 parcelURL로 사용하여 다른 애플리케이션에 주입
$ npm install
$ npm run build
-
React
import Parcel from "single-spa-react/parcel"; export function myComponent(): React.ReactElement { const [parcelConfig, setParcelConfig] = useState<any>(null); useEffect(() => { const loadParcel = async () => { const { parcel: config } = await import(parcelURL); setParcelConfig(config); }; loadParcel(); }, []); return parcelConfig ? <Parcel config={parcelConfig} /> : <div>Loading...</div>; }
-
Vue
<template> <Parcel :config="parcelConfig" :mountParcel="mountParcel" /> </template> <script lang="ts"> import Parcel from "single-spa-vue/parcel"; import { mountRootParcel } from "single-spa"; export default { components: { Parcel }, data() { return { parcelConfig: import(parcelURL).then((module) => module.parcel), mountParcel: mountRootParcel, }; }, }; </script>
-
Svelte
<script lang="ts"> import { onMount } from "svelte"; import { mountRootParcel } from "single-spa"; let container: HTMLDivElement; onMount(() => { let parcel: any; const loadParcel = async () => { const { parcel: parcelConfig } = await import(parcelURL); parcel = mountRootParcel(parcelConfig, { domElement: container, }); }; loadParcel(); return () => { if (parcel) { parcel.unmount(); } }; }); </script> <div bind:this={container}></div>
-
getTokens 함수
현재 로그인되어있는 사용자의 토큰 3종을 읽어옵니다.
이 기능은 동일 도메인에서 로그인되어 있어야 작동합니다.const { getTokens } = await import(parcelURL); const { idToken, accessToken, refreshToken } = await getTokens();
@pickme/auth
를 위젯 형태로 페이지에 삽입하여,
사용자 관리 애플리케이션을 페이지에 통합할 필요 없이 개발 페이지에서도 사용할 수 있도록 합니다.
-
유저 스크립트 관리자 설치
- Chrome: Tampermonkey, Violentmonkey
- Firefox: Greasemonkey, Tampermonkey, Violentmonkey
- Safari: Tampermonkey, Userscripts
- Microsoft Edge: Tampermonkey, Violentmonkey
- Opera: Tampermonkey, Violentmonkey
- Maxthon: Violentmonkey
- AdGuard: (추가 소프트웨어가 필요하지 않습니다)
-
스크립트 다운로드 (스크립트 관리자를 설치했으면 자동으로 감지합니다.)
-
개발 서버로 열린
http://localhost
도메인에서 자동으로@pickme/auth
가 페이지에 삽입됩니다.