Skip to content

카카오 로그인,로그아웃 기능 구현 및 홈 스택페이지 구현#15

Merged
thswogh merged 16 commits intomainfrom
20250603_#9_6월4일_기능추가_소셜로그인_카카오_로그인
Jul 2, 2025

Hidden character warning

The head ref may contain hidden characters: "20250603_#9_6\uc6d44\uc77c_\uae30\ub2a5\ucd94\uac00_\uc18c\uc15c\ub85c\uadf8\uc778_\uce74\uce74\uc624_\ub85c\uadf8\uc778"
Merged

카카오 로그인,로그아웃 기능 구현 및 홈 스택페이지 구현#15
thswogh merged 16 commits intomainfrom
20250603_#9_6월4일_기능추가_소셜로그인_카카오_로그인

Conversation

@thswogh
Copy link
Contributor

@thswogh thswogh commented Jun 27, 2025

#9

Summary by CodeRabbit

  • New Features

    • 홈, 아카이브, 업로드, 우수작, 마이 탭으로 구성된 탭 기반 내비게이션 추가
    • 카카오, 네이버, 구글 소셜 로그인 버튼 추가 및 카카오 로그인 기능 구현
    • 전역 사용자 상태 관리를 위한 인증 컨텍스트 도입
    • 홈, 아카이브, 업로드, 우수작, 프로필, 로그인 전용 페이지 생성
    • 로그아웃 기능 및 사용자 프로필 정보 표시 기능 추가
  • Chores

    • Expo, Babel, Metro, Tailwind CSS, 환경 변수 등 구성 파일 추가 및 업데이트
    • 신규 기능과 스타일링 지원을 위한 의존성 업데이트
    • GitHub Actions 워크플로우 및 .gitignore 파일 개선
  • Style

    • Tailwind CSS 통합으로 앱 전반에 일관된 스타일 적용
  • Refactor

    • 사용자 인증 상태에 따른 내비게이션 로직 개선으로 원활한 사용자 경험 제공
  • Bug Fixes

    • 아이콘 자산의 import 경로 수정

@thswogh thswogh requested a review from meteorqz6 June 27, 2025 07:31
@coderabbitai
Copy link

coderabbitai bot commented Jun 27, 2025

Caution

Review failed

The pull request is closed.

## Walkthrough

이번 변경은 React Native Expo 프로젝트에 환경 설정, Babel 및 Metro 구성, Tailwind CSS 통합, 인증 컨텍스트 도입, 탭 기반 내비게이션 레이아웃 구현, 로그인 및 각 탭별 페이지 컴포넌트 추가, 그리고 Kakao, Naver, Google 소셜 로그인 UI 컴포넌트와 Kakao OAuth WebView 로그인 흐름을 포함한다.

## Changes

| File(s)                                                                 | Change Summary                                                                                                    |
|-------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
| .env, .eslintignore, .prettierignore, global.css                        | 환경 변수 파일 추가 및 ESLint/Prettier 무시 파일 수정, Tailwind CSS 글로벌 스타일 추가                          |
| app.config.js, app.json, babel.config.js, metro.config.js, tailwind.config.js | Expo, Babel, Metro, Tailwind CSS 설정 파일 추가/수정 및 app.json 삭제                                           |
| package.json, tsconfig.json                                             | 의존성 추가 및 업데이트, TypeScript 경로 및 컴파일러 옵션 수정                                                  |
| .gitignore, .github/workflows/DAILYSNAP-FE-PR-CI.yml                    | yarn.lock 무시 추가 및 CI 워크플로우에서 캐시 옵션 제거                                                         |
| app/_layout.tsx, app/(tabs)/_layout.tsx                                 | 루트 및 탭 내비게이션 레이아웃 구현, 컨텍스트 프로바이더 추가, StatusBar 포함                                  |
| app/(tabs)/home.tsx, app/(tabs)/archive.tsx, app/(tabs)/upload.tsx, app/(tabs)/awards.tsx, app/(tabs)/profile.tsx | 각 탭별 화면 컴포넌트 래퍼로 추가                                                                                |
| app/index.tsx                                                          | 인증 상태에 따른 리다이렉트 로직 및 로딩 인디케이터 구현                                                        |
| app/login.tsx, pages/auth/LoginPage.tsx                                 | 로그인 라우트 및 로그인 페이지, 소셜 로그인 버튼 핸들러 추가                                                    |
| app/login/index.tsx, features/auth/hooks/useLogin.ts                    | 미사용 로그인 컴포넌트 및 훅 파일 삭제                                                                           |
| features/auth/model/AuthContext.tsx, features/auth/model/types.ts        | 인증 컨텍스트, 타입, 프로바이더 컴포넌트 추가                                                                   |
| features/auth/ui/KakaoLoginButton.tsx, features/auth/ui/KakaoLoginWebView.tsx | Kakao 소셜 로그인 버튼과 OAuth WebView 로그인 흐름 구현                                                        |
| features/auth/ui/NaverLoginButton.tsx, features/auth/ui/GoogleLoginButton.tsx | Naver, Google 소셜 로그인 버튼 UI 컴포넌트 추가 (로직 미구현)                                                    |
| features/auth/ui/SocialLoginButtons.tsx                                 | 모든 소셜 로그인 버튼을 렌더링하도록 리팩토링                                                                    |
| pages/archive/ArchivePage.tsx, pages/awards/AwardsPage.tsx, pages/home/HomePage.tsx, pages/profile/ProfilePage.tsx, pages/upload/UploadPage.tsx | 각 앱 섹션별 플레이스홀더 페이지 컴포넌트 추가                                                                  |
| shared/ui/icon/index.tsx                                                | 아이콘 모듈 임포트 경로 수정                                                                                      |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant App
    participant AuthContext
    participant Router
    participant KakaoLoginWebView
    participant KakaoAPI

    User->>App: 앱 실행
    App->>AuthContext: userInfo 확인 요청
    AuthContext-->>App: userInfo 반환 (존재 여부)
    App->>Router: (홈 탭) 또는 (로그인)으로 리다이렉트
    User->>LoginPage: 카카오 로그인 버튼 클릭
    LoginPage->>KakaoLoginWebView: WebView 모달 표시
    KakaoLoginWebView->>KakaoAPI: OAuth 코드 요청
    KakaoAPI-->>KakaoLoginWebView: 코드 포함 리다이렉트
    KakaoLoginWebView->>KakaoAPI: 코드로 액세스 토큰 요청
    KakaoAPI-->>KakaoLoginWebView: 액세스 토큰 반환
    KakaoLoginWebView->>KakaoAPI: 사용자 정보 요청
    KakaoAPI-->>KakaoLoginWebView: 사용자 정보 반환
    KakaoLoginWebView->>LoginPage: onLoginSuccess(userInfo) 호출
    LoginPage->>AuthContext: login(userInfo) 호출
    LoginPage->>Router: 홈 탭으로 리다이렉트

Possibly related PRs

  • fix: build 오류 해결 #7: app.json 파일의 일부 이미지 관련 속성 제거 변경과 본 PR의 app.json 삭제 및 app.config.js 도입이 관련됨.

Poem

🐇
새 페이지와 설정이 맞물려,
탭과 로그인 줄 맞춰 세워.
카카오 뛰어들어 OAuth 춤,
Tailwind 바람이 스타일 품.
컨텍스트와 경로 새로워,
토끼가 자랑하는 우리 일!
🌱✨


<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->

---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: .coderabbit.yaml**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 85253d6d5dd87e860f2c864dbe8f6cb74b08b55e and 7920d04656a3cf3acc49c4ae833d854f50f0fd7e.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `.github/workflows/DAILYSNAP-FE-PR-CI.yml` (1 hunks)

</details>

</details>
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Team-DailySnap/DailySnap-FE&utm_content=15):

- 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](https://www.coderabbit.ai/contact-us/support) 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](https://docs.coderabbit.ai/finishing-touches/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.

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

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: 7

🔭 Outside diff range comments (1)
.env (1)

1-3: Sensitive credentials committed to VCS – remove immediately

The Kakao REST API key is publicly exposed in the repo history. Anyone can now abuse your quota or impersonate your app.

  1. Rotate / revoke the leaked key in the Kakao Developers console ASAP.
  2. Add .env to .gitignore.
  3. Commit a safe placeholder file (.env.example) without real secrets.

Proposed quick fix:

-.env
+.env
+.env.*

…then move these values to a secure secrets store (GitHub Actions, Expo server variables, etc.).

🧹 Nitpick comments (27)
.prettierignore (1)

5-6: Possible redundancy between build and dist entries

If your CI/CD pipeline generates either build/ or dist/ (but not both), including both entries is harmless but redundant noise. Consider keeping only the folder that actually appears in this repository to avoid future confusion.

app/(tabs)/home.tsx (2)

1-2: Remove redundant React import

With the automatic JSX transform shipped in React 17+ (supported by Expo SDK 49+), explicit import React from "react" is no longer required.
Eliminating it reduces noise without affecting functionality.

-import React from "react";
+// No import needed – JSX runtime auto-injects React types

4-6: Consider deduplicating one-liner tab wrappers

All five tab files repeat the same “wrapper that just returns PageComponent” pattern. You could DRY this up by exporting the page module directly in app/(tabs) or generating the wrappers dynamically:

// e.g. app/(tabs)/home.tsx
export { default } from "../../pages/home/HomePage";

This keeps routing intact while removing ~30 LOC across the repo.

app/(tabs)/upload.tsx (2)

1-2: Unnecessary React import

Same rationale as the HomeTab file—React import may be safely dropped.

-import React from "react";
+// JSX runtime provides React automatically

4-6: Duplicate wrapper – opportunity to re-export

See previous comment in home.tsx for a concise re-export pattern that removes boilerplate.

app/login.tsx (2)

1-2: Drop superfluous React import

-import React from "react";
+// React import not required with new JSX transform

4-6: Minor naming consistency

Other tab entry files are suffixed with Tab (e.g., HomeTab). While this file isn’t inside (tabs), aligning naming (LoginScreen or LoginPageEntry) might avoid confusion between wrapper vs. page components.

app/(tabs)/profile.tsx (2)

1-2: React import can be removed

Same as other wrappers.

-import React from "react";

4-6: Boilerplate duplication

Re-export pattern recommended earlier applies here too.

app/(tabs)/awards.tsx (2)

1-2: Remove redundant React import

-import React from "react";

4-6: Duplicate wrapper

Same DRY recommendation as other tabs.

app/(tabs)/archive.tsx (1)

4-6: Tiny nit – add an explicit displayName or use an arrow for consistency

Most other tab wrappers are anonymous arrow functions (const FooTab = () => …). Aligning styles avoids diff-noise later.

-export default function ArchiveTab() {
-  return <ArchivePage />;
-}
+export const ArchiveTab: React.FC = () => <ArchivePage />;
pages/archive/ArchivePage.tsx (2)

8-10: Remove the empty className attribute

The empty attribute is a no-op and clutters the JSX.

-        <Text className="">아카이브</Text>
+        <Text>아카이브</Text>

1-13: Consider extracting the repeated skeleton into a shared component

ArchivePage, UploadPage, and AwardsPage are identical except for the label. A small helper avoids duplication:

// ui/CenteredLabelPage.tsx
export const CenteredLabelPage: React.FC<{ label: string }> = ({ label }) => (
  <SafeAreaView className="flex-1 bg-white">
    <View className="items-center mb-10">
      <Text>{label}</Text>
    </View>
  </SafeAreaView>
);

Each page then becomes:

export default function ArchivePage() {
  return <CenteredLabelPage label="아카이브" />;
}
pages/upload/UploadPage.tsx (1)

8-10: Drop the empty className on <Text>

Same clean-up as in ArchivePage.

-        <Text className="">업로드</Text>
+        <Text>업로드</Text>
pages/awards/AwardsPage.tsx (1)

8-10: Drop the empty className on <Text>

-        <Text className="">어워드</Text>
+        <Text>어워드</Text>
app/_layout.tsx (1)

16-16: Consider removing the styled export from the root layout.

The nativewind styled export is typically not needed in the root layout file. If it's required elsewhere, consider exporting it from a dedicated utility file instead.

-export { default as styled } from "nativewind";
features/auth/ui/GoogleLoginButton.tsx (1)

5-5: Props are properly typed but unused.

The component accepts onLoginSuccess and onLoginError callbacks but doesn't use them yet. This is acceptable for scaffolding but should be implemented before production use.

Would you like me to help implement the Google OAuth login flow similar to the Kakao implementation?

features/auth/ui/NaverLoginButton.tsx (1)

5-11: Implement the missing login functionality.

The component accepts onLoginSuccess and onLoginError callbacks but doesn't implement any actual login logic or event handling. The TouchableOpacity lacks an onPress handler.

This appears to be a placeholder implementation. Would you like me to help implement the Naver login flow similar to the Kakao implementation, or should this be tracked as a TODO for future development?

 export const NaverLoginButton: React.FC<SocialLoginProps> = ({ onLoginSuccess, onLoginError }) => {
+  const handleNaverLogin = () => {
+    // TODO: Implement Naver login logic
+    onLoginError("Naver login not yet implemented");
+  };
+
   return (
-    <TouchableOpacity>
+    <TouchableOpacity onPress={handleNaverLogin}>
       <Text>네이버로 시작하기</Text>
     </TouchableOpacity>
   );
 };
pages/home/HomePage.tsx (1)

19-19: Fix spacing in className.

There's a double space in the Tailwind className which should be cleaned up.

-      <TouchableOpacity className="bg-red-400 p-4  items-center" onPress={handleLogout}>
+      <TouchableOpacity className="bg-red-400 p-4 items-center" onPress={handleLogout}>
pages/auth/LoginPage.tsx (1)

4-6: Standardize import path style for consistency.

The imports mix path aliases (@/) with relative paths (../../). Consider using consistent path aliases throughout for better maintainability.

 import { SocialLoginButtons } from "@/features/auth/ui/SocialLoginButtons";
-import { useAuth } from "../../features/auth/model/AuthContext";
-import type { UserInfo } from "../../features/auth/model/types";
+import { useAuth } from "@/features/auth/model/AuthContext";
+import type { UserInfo } from "@/features/auth/model/types";
app/index.tsx (3)

1-1: Specify which ESLint rules to disable rather than using a blanket disable.

The global /*eslint-disable */ comment disables all ESLint rules for the entire file. This can hide potential issues and makes the code harder to maintain.

-/*eslint-disable */
+/* eslint-disable react-hooks/exhaustive-deps */

11-23: Consider removing the arbitrary 100ms delay and improve error handling.

The 100ms timeout appears unnecessary and could create a poor user experience with an extra loading state. Additionally, there's no error handling if the useAuth hook fails.

  useEffect(() => {
-    const timer = setTimeout(() => {
-      if (userInfo) {
-        // 로그인된 사용자면, 홈 네비게이션으로 이동
-        router.replace("/(tabs)/home");
-      } else {
-        // 로그인되지 않았으면 로그인 페이지로 이동
-        router.replace("/login");
-      }
-    }, 100);
-
-    return () => clearTimeout(timer);
+    if (userInfo) {
+      // 로그인된 사용자면, 홈 네비게이션으로 이동
+      router.replace("/(tabs)/home");
+    } else {
+      // 로그인되지 않았으면 로그인 페이지로 이동
+      router.replace("/login");
+    }
  }, [userInfo, router]);

28-28: Consider using theme colors instead of hardcoded color values.

The hardcoded color #195B35 should be defined in a theme or constants file for better maintainability.

-      <ActivityIndicator size="large" color="#195B35" />
+      <ActivityIndicator size="large" color="#195B35" />

Consider defining this color in a theme constants file and importing it here.

pages/profile/ProfilePage.tsx (1)

6-17: Enhance the profile page styling and layout for better user experience.

The current implementation is very minimal and may not provide a good user experience. Consider adding more comprehensive profile information display and better styling.

export default function ProfilePage() {
  const { userInfo } = useAuth();

  return (
    <SafeAreaView className="flex-1 bg-white">
-      <View className="items-center mb-10">
-        <Text>{userInfo?.nickname || "사용자 닉네임"}</Text>
-        <Text>{userInfo?.email || "이메일 없음"}</Text>
+      <View className="flex-1 px-4 py-6">
+        <View className="items-center mb-8">
+          <View className="w-24 h-24 bg-gray-200 rounded-full mb-4" />
+          <Text className="text-xl font-semibold text-gray-900">
+            {userInfo?.nickname || "사용자 닉네임"}
+          </Text>
+          <Text className="text-sm text-gray-600">
+            {userInfo?.email || "이메일 없음"}
+          </Text>
+        </View>
+        {/* Add more profile sections here */}
      </View>
    </SafeAreaView>
  );
}
features/auth/ui/KakaoLoginButton.tsx (1)

19-23: Improve error parameter typing.

Consider using a more specific type for the error parameter to enhance type safety.

-const handleLoginError = (error: any) => {
+const handleLoginError = (error: unknown) => {

The current type checking logic for the error is already good, but unknown is safer than any.

features/auth/ui/KakaoLoginWebView.tsx (1)

114-114: Remove unused injectedJavaScript.

The injectedJavaScript prop appears to be unused and can be removed for cleaner code.

-injectedJavaScript="window.ReactNativeWebView.postMessage('')"
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 518c87c and d10d2bf.

⛔ Files ignored due to path filters (6)
  • package-lock.json is excluded by !**/package-lock.json
  • shared/assets/fonts/SpaceMono-Regular.ttf is excluded by !**/*.ttf
  • shared/assets/icons/search.svg is excluded by !**/*.svg
  • shared/assets/images/auth/authBackground.png is excluded by !**/*.png
  • shared/assets/images/auth/kakaoLogo.png is excluded by !**/*.png
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (36)
  • .env (1 hunks)
  • .eslintignore (1 hunks)
  • .prettierignore (1 hunks)
  • app.config.js (1 hunks)
  • app.json (1 hunks)
  • app/(tabs)/_layout.tsx (1 hunks)
  • app/(tabs)/archive.tsx (1 hunks)
  • app/(tabs)/awards.tsx (1 hunks)
  • app/(tabs)/home.tsx (1 hunks)
  • app/(tabs)/profile.tsx (1 hunks)
  • app/(tabs)/upload.tsx (1 hunks)
  • app/_layout.tsx (1 hunks)
  • app/index.tsx (1 hunks)
  • app/login.tsx (1 hunks)
  • app/login/index.tsx (0 hunks)
  • babel.config.js (1 hunks)
  • features/auth/hooks/useLogin.ts (0 hunks)
  • features/auth/model/AuthContext.tsx (1 hunks)
  • features/auth/model/types.ts (1 hunks)
  • features/auth/ui/GoogleLoginButton.tsx (1 hunks)
  • features/auth/ui/KakaoLoginButton.tsx (1 hunks)
  • features/auth/ui/KakaoLoginWebView.tsx (1 hunks)
  • features/auth/ui/NaverLoginButton.tsx (1 hunks)
  • features/auth/ui/SocialLoginButtons.tsx (1 hunks)
  • global.css (1 hunks)
  • metro.config.js (1 hunks)
  • package.json (1 hunks)
  • pages/archive/ArchivePage.tsx (1 hunks)
  • pages/auth/LoginPage.tsx (1 hunks)
  • pages/awards/AwardsPage.tsx (1 hunks)
  • pages/home/HomePage.tsx (1 hunks)
  • pages/profile/ProfilePage.tsx (1 hunks)
  • pages/upload/UploadPage.tsx (1 hunks)
  • shared/ui/icon/index.tsx (1 hunks)
  • tailwind.config.js (1 hunks)
  • tsconfig.json (1 hunks)
💤 Files with no reviewable changes (2)
  • app/login/index.tsx
  • features/auth/hooks/useLogin.ts
🧰 Additional context used
🧬 Code Graph Analysis (16)
app/(tabs)/upload.tsx (1)
pages/upload/UploadPage.tsx (1)
  • UploadPage (5-13)
app/(tabs)/archive.tsx (1)
pages/archive/ArchivePage.tsx (1)
  • ArchivePage (5-13)
app/(tabs)/awards.tsx (1)
pages/awards/AwardsPage.tsx (1)
  • AwardsPage (5-13)
app/login.tsx (1)
pages/auth/LoginPage.tsx (1)
  • LoginPage (8-36)
app/(tabs)/home.tsx (1)
pages/home/HomePage.tsx (1)
  • HomePage (7-24)
app/(tabs)/profile.tsx (1)
pages/profile/ProfilePage.tsx (1)
  • ProfilePage (6-17)
features/auth/ui/NaverLoginButton.tsx (1)
features/auth/model/types.ts (1)
  • SocialLoginProps (9-12)
features/auth/model/AuthContext.tsx (1)
features/auth/model/types.ts (1)
  • UserInfo (1-7)
pages/home/HomePage.tsx (1)
features/auth/model/AuthContext.tsx (1)
  • useAuth (24-31)
pages/auth/LoginPage.tsx (3)
features/auth/model/AuthContext.tsx (1)
  • useAuth (24-31)
features/auth/model/types.ts (1)
  • UserInfo (1-7)
features/auth/ui/SocialLoginButtons.tsx (1)
  • SocialLoginButtons (8-24)
app/index.tsx (1)
features/auth/model/AuthContext.tsx (1)
  • useAuth (24-31)
pages/profile/ProfilePage.tsx (1)
features/auth/model/AuthContext.tsx (1)
  • useAuth (24-31)
app/_layout.tsx (2)
shared/api/query-client.ts (1)
  • queryClient (3-3)
features/auth/model/AuthContext.tsx (1)
  • AuthProvider (38-58)
features/auth/ui/SocialLoginButtons.tsx (4)
features/auth/model/types.ts (1)
  • SocialLoginProps (9-12)
features/auth/ui/KakaoLoginButton.tsx (1)
  • KakaoLoginButton (6-47)
features/auth/ui/NaverLoginButton.tsx (1)
  • NaverLoginButton (5-11)
features/auth/ui/GoogleLoginButton.tsx (1)
  • GoogleLoginButton (5-11)
features/auth/ui/KakaoLoginButton.tsx (2)
features/auth/model/types.ts (1)
  • SocialLoginProps (9-12)
features/auth/ui/KakaoLoginWebView.tsx (1)
  • KakaoLoginWebView (19-137)
features/auth/ui/GoogleLoginButton.tsx (1)
features/auth/model/types.ts (1)
  • SocialLoginProps (9-12)
🔇 Additional comments (23)
.eslintignore (1)

5-6: 👍 Excluding dist and tailwind.config.js makes sense

Ignoring compiled artifacts and Tailwind’s configuration file will speed up linting and avoid false positives.

shared/ui/icon/index.tsx (1)

1-1: Verify TS path alias & Metro config after import-path change

The alias @/shared/assets/icons must now be reflected in:

  1. tsconfig.jsonpaths
  2. babel.config.js / metro.config.js (if you rely on module-resolver)

Failure to update all places will break iOS/Android builds but not be caught by type-checking alone.

global.css (1)

1-3: Tailwind base/components/utilities imported correctly

Nothing to flag—this is the standard minimal Tailwind boilerplate.

app/(tabs)/archive.tsx (1)

2-2: Double-check the relative import path

"../../pages/archive/ArchivePage" assumes that pages/ sits at the repo root (two levels up from app/(tabs)/) which matches the current layout.
If you later move either folder, this will silently break. Consider using an alias (@pages/...) via tsconfig.json / babel paths to make moves painless.

app.json (1)

29-29: Verify Kakao plugin configuration

Adding @react-native-kakao/core is necessary for Kakao login, but:

  1. The plugin expects matching Android/iOS native settings (redirect scheme, manifest queries, App Key in strings.xml, etc.).
  2. Ensure the scheme ("myapp") lines up with Kakao redirect URI; otherwise login will fail.

Please cross-check the official plugin docs or run the following to confirm the plugin has been autolinked:

#!/bin/bash
# List kakao packages in the autolinked config
grep -R "@react-native-kakao" -n android/ ios/ | head
metro.config.js (1)

1-9: LGTM - Well-configured Metro setup for Expo with Tailwind CSS.

The Metro configuration correctly extends the default Expo config and appropriately adds CSS support for Tailwind integration. The asset extensions cover all common image formats needed for a React Native app.

app/_layout.tsx (1)

20-34: LGTM - Proper provider hierarchy and navigation structure.

The provider nesting order is correct:

  • SafeAreaProvider (outermost for safe area handling)
  • QueryClientProvider (for data fetching)
  • AuthProvider (for authentication state)

The Stack navigation setup appropriately handles the authentication flow with dedicated screens for login and the main tab navigation.

app/(tabs)/_layout.tsx (1)

1-46: LGTM - Clean and well-structured tab navigation.

The tab layout is properly configured with:

  • Appropriate screen names and Korean titles
  • Consistent structure across all tabs
  • Header disabled for custom styling flexibility

The five-tab structure (홈, 아카이브, 업로드, 우수작, 마이) provides good coverage for the app's core features.

tailwind.config.js (1)

3-15: LGTM - Comprehensive Tailwind configuration for React Native.

The configuration properly:

  • Covers all source directories with appropriate glob patterns
  • Includes both JavaScript and TypeScript file extensions
  • Uses standard Tailwind structure with empty theme extensions (ready for customization)
  • Appropriate for React Native with NativeWind setup
pages/home/HomePage.tsx (1)

8-14: LGTM - Clean logout implementation.

The logout flow is well-implemented: calls the auth context's logout method and properly replaces the route to prevent back navigation to the authenticated area.

package.json (1)

15-32: LGTM - Dependencies align with implemented features.

The new dependencies properly support the features being implemented:

  • axios for HTTP requests (likely for auth API calls)
  • nativewind/tailwindcss for the Tailwind styling used throughout the components
  • react-native-safe-area-context used in HomePage
  • react-native-webview supports the Kakao login WebView implementation
  • babel-plugin-module-resolver enables the path aliases configured in tsconfig.json
tsconfig.json (1)

6-10: LGTM - Configuration supports the project structure.

The TypeScript configuration changes properly support the new project structure:

  • Simplified path alias makes imports cleaner
  • baseUrl establishes proper module resolution
  • jsx: "react-native" is correct for this React Native project
  • Updated typeRoots suggests proper type organization
pages/auth/LoginPage.tsx (2)

12-16: LGTM - Solid login success flow.

The login success handler properly:

  1. Logs the success for debugging
  2. Updates the auth context with user info
  3. Navigates to home tab using replace to prevent back navigation

18-21: LGTM - Good error handling with user feedback.

The error handler provides both developer logging and user-friendly alert messaging, which is essential for debugging and user experience.

babel.config.js (1)

1-19: LGTM! Babel configuration is well-structured.

The Babel configuration correctly includes the necessary presets and plugins for the project:

  • babel-preset-expo for Expo support
  • nativewind/babel for Tailwind CSS integration
  • module-resolver with proper alias and platform-specific file extensions
app.config.js (1)

1-37: LGTM! Comprehensive Expo configuration with proper platform settings.

The configuration properly defines:

  • App metadata and branding
  • Platform-specific settings for iOS and Android
  • Custom URI scheme for deep linking
  • Necessary plugins for routing and fonts
  • Experimental typed routes feature
features/auth/ui/SocialLoginButtons.tsx (1)

8-24: LGTM! Clean container component with proper TypeScript integration.

The component effectively serves as a container for social login buttons with:

  • Proper TypeScript typing using SocialLoginProps
  • Clean callback propagation to individual button components
  • Good styling with Tailwind CSS classes
  • Clear separation of concerns

The integration with the individual button components looks well-structured based on the related code snippets.

features/auth/model/types.ts (1)

1-14: Well-structured type definitions.

The TypeScript interfaces and types are well-designed with appropriate field types and proper use of optional properties. The callback function signatures in SocialLoginProps provide clear contracts for social login components.

features/auth/ui/KakaoLoginButton.tsx (1)

38-44: Modal implementation looks good.

The modal configuration with slide animation and pageSheet presentation style provides a good user experience for the OAuth flow.

features/auth/model/AuthContext.tsx (2)

24-31: Excellent error handling for context usage.

The custom hook properly validates that it's being used within the AuthProvider and throws a descriptive error message. This is a best practice for React context hooks.


38-58: Clean AuthProvider implementation.

The provider component follows React patterns well with proper state management and context value provision. The derived isLoggedIn boolean is a nice touch for convenience.

features/auth/ui/KakaoLoginWebView.tsx (2)

28-80: OAuth implementation follows best practices.

The two-step OAuth flow (authorization code → access token → user info) is implemented correctly according to Kakao's OAuth specification. The error handling and token exchange logic are well-structured.


83-101: Proper URL parsing and navigation handling.

The navigation state change handler correctly identifies the OAuth callback URL and extracts the authorization code. The URL parsing logic is solid and handles the OAuth redirect properly.

import { SafeAreaProvider } from "react-native-safe-area-context";
import { queryClient } from "../shared/api/query-client";
import { AuthProvider } from "../features/auth/model/AuthContext";
import "../global.css";
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Move global CSS import to the top of the file.

Global CSS imports should be placed before other imports to ensure styles are loaded first and avoid potential timing issues.

+import "../global.css";
 import NetInfo from "@react-native-community/netinfo";
 import { onlineManager, QueryClientProvider } from "@tanstack/react-query";
 import { Stack } from "expo-router";
 import { StatusBar } from "expo-status-bar";
 import { SafeAreaProvider } from "react-native-safe-area-context";
 import { queryClient } from "../shared/api/query-client";
 import { AuthProvider } from "../features/auth/model/AuthContext";
-import "../global.css";
🤖 Prompt for AI Agents
In app/_layout.tsx at line 8, move the import statement for "../global.css" to
the very top of the file before any other imports to ensure global styles are
loaded first and prevent timing issues.

Comment on lines +7 to +10
<TouchableOpacity>
<Text className="">구글로 시작하기</Text>
</TouchableOpacity>
);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Implement touch event handling and login logic.

The button currently lacks:

  1. onPress handler to trigger login flow
  2. Login implementation (similar to KakaoLoginButton)
  3. Styling (empty className attribute)
 <TouchableOpacity
+  onPress={() => {
+    // TODO: Implement Google login logic
+    onLoginError("Google login not implemented yet");
+  }}
 >
-  <Text className="">구글로 시작하기</Text>
+  <Text className="text-center py-3 px-6 bg-blue-500 text-white rounded-lg">
+    구글로 시작하기
+  </Text>
 </TouchableOpacity>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<TouchableOpacity>
<Text className="">구글로 시작하기</Text>
</TouchableOpacity>
);
<TouchableOpacity
onPress={() => {
// TODO: Implement Google login logic
onLoginError("Google login not implemented yet");
}}
>
<Text className="text-center py-3 px-6 bg-blue-500 text-white rounded-lg">
구글로 시작하기
</Text>
</TouchableOpacity>
);
🤖 Prompt for AI Agents
In features/auth/ui/GoogleLoginButton.tsx around lines 7 to 10, the
TouchableOpacity component is missing an onPress handler to initiate the Google
login flow, the actual login logic similar to KakaoLoginButton, and proper
styling instead of an empty className. Add an onPress prop that triggers the
Google login function, implement the login logic inside that handler, and
replace the empty className with appropriate styles for the button text.

setShowWebView(true);
};

const handleLoginSuccess = (userInfo: any) => {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use proper TypeScript types instead of any.

The parameter should use the UserInfo type from the imported types file for better type safety.

-const handleLoginSuccess = (userInfo: any) => {
+const handleLoginSuccess = (userInfo: UserInfo) => {

Don't forget to import the UserInfo type:

-import type { SocialLoginProps } from "../model/types";
+import type { SocialLoginProps, UserInfo } from "../model/types";
🤖 Prompt for AI Agents
In features/auth/ui/KakaoLoginButton.tsx at line 13, replace the parameter type
'any' in the handleLoginSuccess function with the proper 'UserInfo' type
imported from the types file. First, import the 'UserInfo' type at the top of
the file, then update the function signature to use 'userInfo: UserInfo' for
better type safety.

Comment on lines +5 to +11
interface UserInfo {
id: number;
email: string;
nickname: string;
profileImage?: string;
accessToken: string;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove duplicate interface definition.

The UserInfo interface is already defined in features/auth/model/types.ts. Import it instead of duplicating the definition to follow DRY principles and ensure consistency.

+import type { UserInfo } from "./types";
+
-// 사용자 정보 타입 정의
-interface UserInfo {
-  id: number;
-  email: string;
-  nickname: string;
-  profileImage?: string;
-  accessToken: string;
-}
-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interface UserInfo {
id: number;
email: string;
nickname: string;
profileImage?: string;
accessToken: string;
}
import type { UserInfo } from "./types";
🤖 Prompt for AI Agents
In features/auth/model/AuthContext.tsx around lines 5 to 11, remove the
duplicate UserInfo interface definition and instead import the UserInfo
interface from features/auth/model/types.ts. This will avoid redundancy and keep
the type definition consistent across the codebase.

}),
});

console.log("토큰 응답:", tokenResponse.data);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove sensitive data from console logs.

These console.log statements are logging sensitive information including access tokens and user data, which could be a security risk in production builds.

-console.log("토큰 응답:", tokenResponse.data);
-console.log("user 응답:", userResponse.data);

Consider using a proper logging solution that can be configured per environment, or remove these logs entirely.

Also applies to: 59-59

🤖 Prompt for AI Agents
In features/auth/ui/KakaoLoginWebView.tsx at lines 47 and 59, the console.log
statements output sensitive information such as access tokens and user data,
posing a security risk. Remove these console.log calls or replace them with a
secure logging mechanism that can be disabled or filtered in production
environments to prevent sensitive data exposure.

},
data: new URLSearchParams({
grant_type: "authorization_code",
client_id: KAKAO_REST_API_KEY!,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add runtime validation for environment variables.

Using non-null assertion (!) on environment variables can cause runtime errors if they're not set.

+if (!KAKAO_REST_API_KEY) {
+  throw new Error("EXPO_PUBLIC_KAKAO_REST_API_KEY is required");
+}
+
const tokenResponse = await axios({
  method: "POST", 
  url: "https://kauth.kakao.com/oauth/token",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded",
  },
  data: new URLSearchParams({
    grant_type: "authorization_code",
-    client_id: KAKAO_REST_API_KEY!,
+    client_id: KAKAO_REST_API_KEY,
    redirect_uri: REDIRECT_URI,
    code,
  }),
});

Add similar validation for the WebView source URI.

Also applies to: 112-112

🤖 Prompt for AI Agents
In features/auth/ui/KakaoLoginWebView.tsx at lines 41 and 112, the code uses
non-null assertions on environment variables which can cause runtime errors if
these variables are undefined. Add runtime checks to verify that
KAKAO_REST_API_KEY and the WebView source URI environment variables are defined
before using them. If they are missing, throw a clear error or handle the case
gracefully to prevent runtime crashes.

Comment on lines +14 to +16
onLoginSuccess: (userInfo: any) => void;
onLoginError: (error: any) => void;
onClose: () => void;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use proper TypeScript types instead of any.

The props interface should use specific types for better type safety and consistency with the rest of the codebase.

+import type { UserInfo } from "../model/types";
+
interface KakaoLoginWebViewProps {
-  onLoginSuccess: (userInfo: any) => void;
-  onLoginError: (error: any) => void;
+  onLoginSuccess: (userInfo: UserInfo) => void;
+  onLoginError: (error: unknown) => void;
  onClose: () => void;
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onLoginSuccess: (userInfo: any) => void;
onLoginError: (error: any) => void;
onClose: () => void;
import type { UserInfo } from "../model/types";
interface KakaoLoginWebViewProps {
onLoginSuccess: (userInfo: UserInfo) => void;
onLoginError: (error: unknown) => void;
onClose: () => void;
}
🤖 Prompt for AI Agents
In features/auth/ui/KakaoLoginWebView.tsx around lines 14 to 16, the props
interface uses 'any' types for onLoginSuccess and onLoginError, which reduces
type safety. Replace 'any' with more specific TypeScript types that accurately
represent the expected userInfo and error objects, aligning with the rest of the
codebase's type definitions for these handlers.

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: 0

🧹 Nitpick comments (1)
.github/workflows/DAILYSNAP-FE-PR-CI.yml (1)

24-26: yarn.lock만 삭제하면 npm 캐시 불일치가 계속 남을 수 있습니다

  • .yarnrc*, .pnp.* 등 Yarn 관련 잔여 파일이 남아 있으면 의도치 않은 의존성 해석이 일어날 수 있습니다.
  • CI에서는 npm install 대신 npm ci를 사용해야 package-lock.json 기반의 재현 가능한 빌드가 보장됩니다.

예시 수정안:

-      - name: 🧹 Remove yarn.lock
-        run: rm -f yarn.lock
+      - name: 🧹 Remove yarn artefacts & clean install
+        run: |
+          rm -f yarn.lock .yarnrc* .pnp.*
+          npm ci --prefer-offline --no-audit --no-fund
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c686f15 and e0bb095.

📒 Files selected for processing (1)
  • .github/workflows/DAILYSNAP-FE-PR-CI.yml (1 hunks)
🔇 Additional comments (1)
.github/workflows/DAILYSNAP-FE-PR-CI.yml (1)

18-22: Node 22.14.0 버전이 GitHub Actions 런너에 아직 배포되지 않았을 가능성

  • 현재 LTS 버전(예: 20.x) 사용을 권장합니다.
  • 지정 버전이 없으면 워크플로가 즉시 실패할 수 있으니, 다음 두 가지 중 하나를 검토해 주세요.
    1. node-version: 20 과 같이 LTS 메이저로 다운그레이드
    2. node-version-file: .nvmrc 로 프로젝트 설정을 따르도록 변경

확인 후 문제가 없으면 그대로 진행해도 됩니다.

@thswogh thswogh merged commit 7f8eaea into main Jul 2, 2025
1 of 2 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jul 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant