Skip to content

Conversation

@Insung-Jo
Copy link

@Insung-Jo Insung-Jo commented Jun 9, 2025

📌 변경 사항 개요

  • 로그인/회원가입 로직 구현

✨ 요약

  • zustand + persist 기반 전역 상태관리 구현
  • 로그인 성공 시 accessToken 및 유저 정보 저장
  • 로그인 API 호출 로직 작성 (/auth/login)
  • 회원가입 API 호출 로직 작성 (/user/signup)
  • axios 인스턴스 및 interceptor 설정 (Authorization 헤더 자동 주입)

📝 상세 내용

  • 구현한 내용 중 핵심적인 부분을 정리했습니다.

useAuthStore

export const useAuthStore = create<AuthState>()(
  persist(
    (set) => ({
      accessToken: null,
      user: null,
      isLoggedIn: false,
      setAccessToken: (token) => set({ accessToken: token, isLoggedIn: true }),
      setUser: (user) => set({ user: user }),
      clearAuthState: () =>
        set({ accessToken: null, user: null, isLoggedIn: false }),
    }),
    {
      name: 'auth-storage',
    },
  ),
)

// 값 호출 시
  const user = useAuthStore((state) => state.user)
  const islogin = useAuthStore((state) => state.isLoggedIn)
  • persist를 통하여 상태를 로컬 스토리지에 자동 저장하여 새로고침해도 상태가 유지되게 구현했습니다.
  • 이 부분은 유진님과 겹치는 부분이 있어서 유진님과의 상의 후 해당 코드를 반영하는 것으로 결정했습니다.

axios

const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL,
})

api.interceptors.request.use(
  (config) => {
    const token = useAuthStore.getState().accessToken
    const publicPaths = [AUTH_ENDPOINT.LOGIN, AUTH_ENDPOINT.SIGNUP]

    if (!publicPaths.includes(config.url || '') && token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  },
  (error) => {
    return Promise.reject(error)
  },
)
  • baseURL은 환경 변수(NEXT_PUBLIC_API_URL)를 사용하여 관리하고 있습니다.
  • 토큰이 필요한 요청에만 Authorization 헤더를 주입하도록 구현했으며, publicPaths에는 스웨거 명세 기준으로 토큰이 필요 없는 API 경로를 등록하여 예외 처리 했습니다.

useAuth

export function useAuth() {
  const { setAccessToken, setUser, clearAuthState } = useAuthStore()

  async function login(data: LoginRequest) {
    const response = await loginApi(data)
    const token = response.data.accessToken
    const user = response.data.user

    if (token && user) {
      setAccessToken(token)
      setUser(user)
    } else {
      throw new Error('유효하지 않은 응답입니다.')
    }
  }

  async function signup(data: SignupRequest) {
    await signupApi(data)
  }

  function logout() {
    clearAuthState()
  }

  return {
    login,
    signup,
    logout,
  }
}
  • 상태 변경 로직을 담당하는 커스텀 훅으로 인증 관련 비즈니스 로직을 캡슐화 했습니다.
  • 별도의 에러 처리는 토스트나 모달 등 UI로 처리할 예정이기 때문에, 해당 훅에서는 별도의 에러 핸들링 없이 상태 변경 로직만 작성했습니다.

🔗 관련 이슈

#18

🖼️ 스크린샷

2025-06-09.140152.mp4

✅ 체크리스트

  • 브랜치 네이밍 컨벤션을 준수했습니다
  • 커밋 컨벤션을 준수했습니다
  • 코드가 프로젝트의 스타일 가이드라인을 준수합니다

💡 참고 사항

  • 로그인/회원 가입은 처음 작업해보다 보니 이상한 점 발견되면 말씀해주세요!
  • 리다이렉션 부분은 다음 작업으로 할 예정입니다!
  • 테스트 페이지는 따로 올리지 않았습니다.

Summary by CodeRabbit

  • 신규 기능

    • 로그인 및 회원가입 기능이 추가되었습니다.
    • 인증 상태를 관리하는 스토어와 커스텀 훅이 도입되어, 로그인/로그아웃/회원가입을 간편하게 사용할 수 있습니다.
  • 변경 사항

    • 사용자 정보 구조가 변경되어, 이름(name) 대신 닉네임(nickname)과 프로필 이미지(profileImageUrl)가 사용됩니다.
    • 기존 사용자 인증 관련 스토어가 제거되고, 새로운 인증 전용 스토어로 통합되었습니다.

@coderabbitai
Copy link

coderabbitai bot commented Jun 9, 2025

Walkthrough

인증 기능이 새롭게 도입되어, 로그인 및 회원가입 API 모듈, 엔드포인트 상수, 타입 정의, 인증 관련 Zustand 스토어, 커스텀 훅이 추가되었습니다. 기존 사용자 스토어는 삭제되었고, User 타입이 닉네임 중심으로 변경되었습니다. Axios 인스턴스에는 인증 토큰 자동 첨부 기능이 추가되었습니다.

Changes

파일(그룹) 변경 요약
src/app/features/auth/api/authApi.ts, src/app/features/auth/api/authEndpoint.ts 인증 API 모듈 및 엔드포인트 상수 추가, 로그인/회원가입 함수 도입
src/app/features/auth/hooks/useAuth.ts 인증 로직을 관리하는 커스텀 훅(useAuth) 추가
src/app/features/auth/store/useAuthStore.ts 인증 상태 관리를 위한 Zustand 스토어 추가
src/app/features/auth/types/auth.type.ts 로그인/회원가입 요청 및 응답, 인증 상태 관련 타입 정의
src/app/shared/lib/axios.ts 인증 토큰 자동 첨부 및 예외 처리 포함 Axios 인스턴스 추가
src/app/shared/store/useUserStore.ts 기존 사용자 인증 상태 관리 스토어 및 타입 정의 삭제
src/app/shared/types/user.type.ts User 타입에서 name → nickname, avatarUrl → profileImageUrl, role 속성 제거 등 구조 변경

Sequence Diagram(s)

sequenceDiagram
    participant 컴포넌트
    participant useAuth(커스텀 훅)
    participant authApi
    participant useAuthStore
    participant Axios

    컴포넌트->>useAuth: login({ email, password })
    useAuth->>authApi: login({ email, password })
    authApi->>Axios: POST /auth/login
    Axios-->>authApi: { accessToken, user }
    authApi-->>useAuth: { accessToken, user }
    useAuth->>useAuthStore: setAccessToken(accessToken), setUser(user)
    useAuth-->>컴포넌트: 로그인 결과 반환

    컴포넌트->>useAuth: signup({ nickname, email, password })
    useAuth->>authApi: signup({ nickname, email, password })
    authApi->>Axios: POST /users
    Axios-->>authApi: { user }
    authApi-->>useAuth: { user }
    useAuth-->>컴포넌트: 회원가입 결과 반환

    컴포넌트->>useAuth: logout()
    useAuth->>useAuthStore: clearAuthState()
Loading

Poem

🥕
인증의 길이 새로 열렸네,
로그인, 회원가입 토끼도 척척!
닉네임으로 인사하고,
토큰은 자동으로 챙겨가요.
오래된 굴은 정리하고,
새로운 스토어에 둥지 틀었네.
오늘도 코드는 깔끔하게, 토끼답게!
🐇

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-06-09T06_15_39_549Z-debug-0.log


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51b6910 and 105f8ac.

📒 Files selected for processing (1)
  • src/app/shared/lib/axios.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/shared/lib/axios.ts
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Insung-Jo Insung-Jo self-assigned this Jun 9, 2025
@Insung-Jo Insung-Jo added the ✨Feat 기능 개발 label Jun 9, 2025
@Insung-Jo Insung-Jo added this to the 1차 구현 기간 milestone Jun 9, 2025
@Insung-Jo Insung-Jo linked an issue Jun 9, 2025 that may be closed by this pull request
5 tasks
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (6)
src/app/shared/lib/axios.ts (2)

5-7: 환경변수 검증 추가를 고려해보세요.

process.env.NEXT_PUBLIC_API_URL이 정의되지 않은 경우에 대한 처리가 없습니다. 런타임 오류를 방지하기 위해 검증 로직을 추가하는 것을 권장합니다.

+const baseURL = process.env.NEXT_PUBLIC_API_URL
+if (!baseURL) {
+  throw new Error('NEXT_PUBLIC_API_URL 환경변수가 설정되지 않았습니다.')
+}
+
 const api = axios.create({
-  baseURL: process.env.NEXT_PUBLIC_API_URL,
+  baseURL,
 })

9-22: 토큰 만료 처리를 위한 응답 인터셉터 추가를 고려해보세요.

현재 요청 인터셉터만 구현되어 있는데, 토큰 만료나 인증 실패 시 자동으로 로그아웃 처리할 수 있는 응답 인터셉터를 추가하면 사용자 경험이 향상됩니다.

 )

+api.interceptors.response.use(
+  (response) => response,
+  (error) => {
+    if (error.response?.status === 401) {
+      useAuthStore.getState().clearAuthState()
+      // 로그인 페이지로 리디렉션 로직 추가 가능
+    }
+    return Promise.reject(error)
+  },
+)
+
 export default api
src/app/features/auth/store/useAuthStore.ts (1)

16-18: persist 설정 개선을 고려해보세요.

보안을 위해 persist 설정에 추가 옵션을 고려해볼 수 있습니다.

     {
       name: 'auth-storage',
+      partialize: (state) => ({ 
+        accessToken: state.accessToken, 
+        user: state.user,
+        isLoggedIn: state.isLoggedIn 
+      }),
+      version: 1,
     },
src/app/features/auth/hooks/useAuth.ts (2)

21-23: 회원가입 성공 후 처리 로직을 추가하는 것을 고려해보세요.

현재 회원가입 함수는 API 호출만 하고 성공 시 추가 처리가 없습니다. 사용자 경험을 위해 다음을 고려해보세요:

  1. 회원가입 성공 시 자동 로그인 처리
  2. 성공 메시지나 리다이렉션 로직
  async function signup(data: SignupRequest) {
-   await signupApi(data)
+   const response = await signupApi(data)
+   // 성공 시 자동 로그인이나 성공 처리 로직 추가 고려
+   return response
  }

5-34: 로딩 상태 관리 추가를 고려해보세요.

인증 작업 중 로딩 상태를 관리하면 사용자 경험이 개선됩니다. Zustand 스토어에 isLoading 상태를 추가하거나 별도의 상태 관리를 고려해보세요.

스토어에 로딩 상태 추가 예시:

// useAuthStore에 추가
isLoading: false,
setLoading: (loading: boolean) => set({ isLoading: loading }),

// useAuth에서 사용
const { setAccessToken, setUser, clearAuthState, setLoading } = useAuthStore()

async function login(data: LoginRequest) {
  setLoading(true)
  try {
    // ... 기존 로직
  } finally {
    setLoading(false)
  }
}
src/app/features/auth/types/auth.type.ts (1)

19-26: SignupResponse 타입 정의를 추가하는 것을 고려해보세요.

AuthState 인터페이스는 잘 정의되어 있지만, 코드의 완성도를 위해 SignupResponse 인터페이스 추가를 고려해보세요.

+ export interface SignupResponse {
+   message?: string
+   user?: User
+ }

이렇게 하면 회원가입 API의 응답 타입도 명확하게 정의할 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac1010e and 8a9cd00.

📒 Files selected for processing (8)
  • src/app/features/auth/api/authApi.ts (1 hunks)
  • src/app/features/auth/api/authEndpoint.ts (1 hunks)
  • src/app/features/auth/hooks/useAuth.ts (1 hunks)
  • src/app/features/auth/store/useAuthStore.ts (1 hunks)
  • src/app/features/auth/types/auth.type.ts (1 hunks)
  • src/app/shared/lib/axios.ts (1 hunks)
  • src/app/shared/store/useUserStore.ts (0 hunks)
  • src/app/shared/types/user.type.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/app/shared/store/useUserStore.ts
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/app/features/auth/api/authApi.ts (2)
src/app/features/auth/types/auth.type.ts (3)
  • LoginRequest (3-6)
  • LoginResponse (8-11)
  • SignupRequest (13-17)
src/app/features/auth/api/authEndpoint.ts (1)
  • AUTH_ENDPOINT (1-4)
src/app/shared/lib/axios.ts (2)
src/app/features/auth/store/useAuthStore.ts (1)
  • useAuthStore (5-20)
src/app/features/auth/api/authEndpoint.ts (1)
  • AUTH_ENDPOINT (1-4)
src/app/features/auth/hooks/useAuth.ts (3)
src/app/features/auth/store/useAuthStore.ts (1)
  • useAuthStore (5-20)
src/app/features/auth/api/authApi.ts (2)
  • login (6-8)
  • signup (10-12)
src/app/features/auth/types/auth.type.ts (2)
  • LoginRequest (3-6)
  • SignupRequest (13-17)
src/app/features/auth/store/useAuthStore.ts (1)
src/app/features/auth/types/auth.type.ts (1)
  • AuthState (19-26)
src/app/features/auth/types/auth.type.ts (1)
src/app/shared/types/user.type.ts (1)
  • User (1-8)
🔇 Additional comments (8)
src/app/features/auth/api/authEndpoint.ts (1)

1-4: API 엔드포인트 중앙 관리 - 좋은 구현입니다!

엔드포인트를 상수 객체로 중앙집중식 관리하는 것은 좋은 패턴입니다. 코드 유지보수성과 일관성을 높여줍니다.

src/app/features/auth/store/useAuthStore.ts (1)

5-20: 인증 상태 관리가 잘 구현되었습니다!

Zustand와 persist 미들웨어를 사용한 상태 관리가 깔끔하게 구현되었습니다. setAccessToken에서 자동으로 isLoggedIn을 true로 설정하는 로직도 좋습니다.

src/app/features/auth/api/authApi.ts (1)

1-12: API 함수 구현이 깔끔합니다!

엔드포인트 상수를 사용하고 타입을 적절히 정의한 API 함수 구현이 좋습니다. 위의 반환 타입 개선사항만 적용하면 더욱 견고한 코드가 될 것입니다.

src/app/shared/types/user.type.ts (1)

1-8: 사용자 인터페이스 변경사항이 잘 구현되었습니다.

User 인터페이스의 변경사항들이 인증 플로우와 잘 맞습니다:

  • namenickname으로 변경이 적절합니다
  • profileImageUrlstring | null 타입이 선택적 프로필 이미지를 잘 표현합니다
  • 불필요한 role 속성이 제거되어 인터페이스가 더 간결해졌습니다
src/app/features/auth/types/auth.type.ts (4)

3-6: 로그인 요청 타입이 잘 정의되었습니다.

필수 필드인 emailpassword가 적절히 정의되어 있습니다.


8-11: 로그인 응답 타입이 적절합니다.

accessTokenuser 정보를 포함한 응답 구조가 명확하게 정의되어 있습니다.


13-17: 회원가입 요청 타입이 잘 구성되었습니다.

nickname, email, password 필드가 적절히 정의되어 있고, 새로운 User 인터페이스와 일관성이 있습니다.


19-26: AuthState 인터페이스가 잘 설계되었습니다.

Zustand 스토어의 상태와 액션 메서드들이 명확하게 타입 정의되어 있어 타입 안전성을 보장합니다.

Copy link

@LeeCh0129 LeeCh0129 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useAuthStore에서 persist 미들웨어를 사용해서 상태관리를 잘 구현해주셨고, axios interceptor로 토큰 처리까지 잘 구현해주셨네요. 👍

Copy link
Contributor

@yuj2n yuj2n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인성님 로그인/회원가입 구현 수고 많으셨고 덕분에 저도 관련 로직을 공부할 수 있었습니당!!

Copy link
Contributor

@dkslel1225 dkslel1225 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로그인/회원가입 설명 감사합니다! 구현하느라 수고 많으셨습니당⛄️👍👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨Feat 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨Feat: 로그인/회원가입 기능 구현

5 participants