Single-Spa Parcel 구성
이 애플리케이션은 Amazon Cognito를 이용해 사용자 회원가입, 로그인, 로그아웃 등의 인증 기능을 제공합니다.
회원가입 시 이메일 인증을 통해 사용자를 확인하며,
로그인 후에는 액세스 토큰, 리프레시 토큰, ID 토큰을 로컬 스토리지에 저장하여 인증 상태를 유지합니다.
안전하고 신뢰할 수 있는 사용자 인증 흐름을 제공합니다.
웹 및 모바일 앱을 위한 자격 증명 플랫폼
사용자 디렉터리, 인증 서버, OAuth 2.0 액세스 토큰 및 자격 증명에 대한 권한 부여 서비스
Cognito 사용자 풀 옵션
환경변수
VITE_COGNITO_USER_POOL_ID= # Cognito 사용자 풀 아이디
VITE_COGNITO_CLIENT_ID= # Cognito 앱클라이언트 아이디
테스트 케이스 통과 여부 및 커버리지 현황 등을 시각적으로 제공합니다.
이 테스트 리포트는 매 릴리즈 업데이트 시 자동으로 최신 상태로 배포됩니다.
프로젝트에서 사용되는 타입 정의를 문서화한 자료입니다.
이 타입 문서는 매 릴리즈 업데이트 시 자동으로 최신 상태로 배포됩니다.
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 요청 (Authorization: Bearer Access Token)
API Gateway ->> Cognito: Access Token 검증 (User Pool Authorizer)
Cognito -->> API Gateway: 검증 결과 (Claim 포함)
note over API Gateway: 백엔드에서 디코딩 하지 않고 전달받은 사용자 Claim 사용
API Gateway ->> Backend: API 요청 전달 (Claim 포함)
Backend -->> API Gateway: 응답 데이터
API Gateway -->> Frontend: 응답 데이터
end
end
graph LR
subgraph CD[🚀 CD 영역]
direction LR
Tag[태그 푸시] --> DeployGH[gh-pages에 배포] --> |자동 워크플로 실행|pages-build-deployment[GitHub Pages 배포 완료]
Tag --> DeployAWS[Amazon S3에 배포] --> |콘텐츠 서빙|CloudFront[Amazon CloudFront]
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"
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
$ 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
를 위젯 형태로 페이지에 삽입하여,
사용자 관리 애플리케이션을 페이지에 통합할 필요 없이 개발 페이지에서도 사용할 수 있도록 합니다.
유저 스크립트 관리자 설치
스크립트 다운로드 (스크립트 관리자를 설치했으면 자동으로 감지합니다.)
개발 서버로 열린 http://localhost
도메인에서 자동으로 @pickme/auth
가 페이지에 삽입됩니다.