diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml
index 94b86985..aff5095d 100644
--- a/.github/workflows/chromatic.yml
+++ b/.github/workflows/chromatic.yml
@@ -36,7 +36,7 @@ jobs:
- name: Chromatic에 게시
id: chromatic
- uses: chromaui/action@v1 # Chromatic의 GitHub Action
+ uses: chromaui/action@latest
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} # 환경 변수 참조
token: ${{ secrets.GITHUB_TOKEN }} # GitHub 인증 토큰
diff --git a/next.config.mjs b/next.config.mjs
index 0310718b..d5464cbd 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -1,11 +1,5 @@
/** @type {import('next').NextConfig} */
-import path from "path";
-import { fileURLToPath } from "url";
-
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = path.dirname(__filename);
-
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
@@ -17,20 +11,20 @@ const nextConfig = {
port: "",
pathname: "/Albaform/**",
},
+ {
+ protocol: "https",
+ hostname: "lh3.googleusercontent.com",
+ port: "",
+ pathname: "/a/**",
+ },
],
},
+ // Webpack 커스터마이징 제거
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ["@svgr/webpack"],
});
-
- config.module.rules.push({
- test: /\.css$/,
- use: ["style-loader", "css-loader", "postcss-loader"],
- include: [path.resolve(__dirname, "node_modules/react-datepicker"), path.resolve(__dirname, "src/app")],
- });
-
return config;
},
};
diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx
index 13c4d836..c215565b 100644
--- a/src/app/(auth)/login/page.tsx
+++ b/src/app/(auth)/login/page.tsx
@@ -63,6 +63,11 @@ export default function LoginPage() {
{isPending ? "로그인 중..." : "로그인"}
+
+
+ SNS 계정으로 로그인하기
+
+
+
+
+ SNS 계정으로 회원가입하기
+
+
+
+
+ SNS 계정으로 회원가입하기
+
+
());
const observer = useRef
(null);
+ const { user } = useUser();
useEffect(() => {
// 클라이언트 환경에서만 IntersectionObserver 실행
@@ -57,11 +59,19 @@ export default function Home() {
한 곳에서 관리하는 알바 구인 플랫폼
-
-
- 알바를 시작하기
-
-
+ {user ? (
+
+
+ 알바 둘러보기
+
+
+ ) : (
+
+
+ 알바를 시작하기
+
+
+ )}
{/* 배경 이미지 */}
diff --git a/src/app/(pages)/albaFormDetail/applicant/[formId]/page.tsx b/src/app/(pages)/albaFormDetail/applicant/[formId]/page.tsx
new file mode 100644
index 00000000..29a68e2a
--- /dev/null
+++ b/src/app/(pages)/albaFormDetail/applicant/[formId]/page.tsx
@@ -0,0 +1,74 @@
+"use client";
+
+import { useParams } from "next/navigation";
+import { useUserFormDetail } from "@/hooks/queries/form/userFormDetail";
+import React, { useEffect, useState } from "react";
+import CardChipIcon from "@/app/components/card/cardList/CardChipIcon";
+import Chip from "@/app/components/chip/Chip";
+
+export default function AlbaFormDetailPage() {
+ const { formId } = useParams(); // useParams로 formId 추출
+ const [formIdState, setFormIdState] = useState(0);
+
+ useEffect(() => {
+ // formId가 문자열로 전달되므로 숫자로 변환하여 상태에 저장
+ if (formId) {
+ setFormIdState(Number(formId)); // formId를 숫자로 변환하여 상태에 저장
+ }
+ }, [formId]);
+
+ // formId가 설정되면 useUserFormDetail 호출
+ const { data, isLoading, error } = useUserFormDetail({ formId: formIdState });
+
+ if (isLoading) {
+ return Loading...
;
+ }
+
+ if (error) {
+ return Error: 데이터를 불러오는데 문제가 발생했습니다.
;
+ }
+
+ if (!data) {
+ return 데이터가 없습니다.
;
+ }
+
+ // 모집 상태 계산
+ const recruitmentStatus = new Date(data.recruitmentEndDate) > new Date() ? "모집중" : "모집완료";
+
+ return (
+
+
사진영역
+
+
+
+
+
+
{new Date(data.createdAt).toLocaleString()} 등록
+
+
+ {data.storeName || "가게명"}
+
+ {data.location || "위치"} ・ {"경력 정보 없음"}
+
+
+
{data.title}
+
+
{data.description}
+
+
+
+ );
+}
diff --git a/src/app/(pages)/albaFormDetail/layout.tsx b/src/app/(pages)/albaFormDetail/layout.tsx
new file mode 100644
index 00000000..af80a7bd
--- /dev/null
+++ b/src/app/(pages)/albaFormDetail/layout.tsx
@@ -0,0 +1,17 @@
+import React, { Suspense } from "react";
+
+export default function AlbaFormDetailLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+ }
+ >
+ {children}
+
+
+ );
+}
diff --git a/src/app/(pages)/albaFormDetail/owner/[id]/page.tsx b/src/app/(pages)/albaFormDetail/owner/[id]/page.tsx
new file mode 100644
index 00000000..8754305e
--- /dev/null
+++ b/src/app/(pages)/albaFormDetail/owner/[id]/page.tsx
@@ -0,0 +1,49 @@
+"use client";
+import CardChipIcon from "@/app/components/card/cardList/CardChipIcon";
+import Chip from "@/app/components/chip/Chip";
+import ChipWithIcon from "@/app/components/chip/ChipWithIcon";
+import React from "react";
+
+// 알바폼 상세 사장님 페이지
+export default function AlbaFormDetailPage({ formId }: { formId: number }) {
+ return (
+
+
사진영역
+
+
+
+
+
+
2024. 05. 04 12:30:54 등록
+
+
+ 코드잇
+ 서울 종로구 ・ 경력 무관
+
+
코드잇 스터디카페 관리 (주말 오전) 모집합니다 서울 종로구 용산구 서대문
+
+
+ 코드잇 스터디 카페입니다. 주말 토, 일 오픈업무 하실 분 구합니다. 성실하게 일하실 분들만 지원 바랍니다.
+ 작성한 이력서(사진 부착)를 알바폼에 첨부해주시고, 아래와 같이 문자 보내주세요. 근무 중 전화통화 불가합니다.
+ 예) OOO입니다. __에 거주합니다. 알바폼 지원. 이력서 검토 후 면접진행자에 한해 면접일정 개별
+ 연락드리겠습니다. 많은 지원 바랍니다.
+
+
+
{/* 오른쪽 콘텐츠 */}
+
+
+ );
+}
diff --git a/src/app/(pages)/albaFormSupportDetail/layout.tsx b/src/app/(pages)/albaFormSupportDetail/layout.tsx
new file mode 100644
index 00000000..af80a7bd
--- /dev/null
+++ b/src/app/(pages)/albaFormSupportDetail/layout.tsx
@@ -0,0 +1,17 @@
+import React, { Suspense } from "react";
+
+export default function AlbaFormDetailLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+ }
+ >
+ {children}
+
+
+ );
+}
diff --git a/src/app/(pages)/albaFormSupportDetail/page.tsx b/src/app/(pages)/albaFormSupportDetail/page.tsx
new file mode 100644
index 00000000..c5df0666
--- /dev/null
+++ b/src/app/(pages)/albaFormSupportDetail/page.tsx
@@ -0,0 +1,44 @@
+import CardChipIcon from "@/app/components/card/cardList/CardChipIcon";
+import Chip from "@/app/components/chip/Chip";
+import React from "react";
+
+// 알바폼 지원내역 상세 페이지
+export default function AlbaFormSupportDetailPage({ formId }: { formId: number }) {
+ return (
+
+
지도영역
+
+
+
+
+
2024. 05. 04 12:30:54 등록
+
+
+ 코드잇
+ 서울 종로구 ・ 경력 무관
+
+
코드잇 스터디카페 관리 (주말 오전) 모집합니다 서울 종로구 용산구 서대문
+
+
+ 코드잇 스터디 카페입니다. 주말 토, 일 오픈업무 하실 분 구합니다. 성실하게 일하실 분들만 지원 바랍니다. 작성한
+ 이력서(사진 부착)를 알바폼에 첨부해주시고, 아래와 같이 문자 보내주세요. 근무 중 전화통화 불가합니다. 예)
+ OOO입니다. __에 거주합니다. 알바폼 지원. 이력서 검토 후 면접진행자에 한해 면접일정 개별 연락드리겠습니다. 많은
+ 지원 바랍니다.
+
+
+
+ );
+}
diff --git a/src/app/(pages)/albaList/page.tsx b/src/app/(pages)/albaList/page.tsx
index c8ecb4c9..a6591cf8 100644
--- a/src/app/(pages)/albaList/page.tsx
+++ b/src/app/(pages)/albaList/page.tsx
@@ -152,7 +152,11 @@ export default function AlbaList() {
{page.data.map((form) => (
))}
diff --git a/src/app/api/oauth/callback/google/route.ts b/src/app/api/oauth/callback/google/route.ts
index 64d21043..809dd5e9 100644
--- a/src/app/api/oauth/callback/google/route.ts
+++ b/src/app/api/oauth/callback/google/route.ts
@@ -4,6 +4,7 @@ import { decodeJwt } from "@/middleware";
import apiClient from "@/lib/apiClient";
import { OauthLoginUser, OauthResponse, OauthSignupUser } from "@/types/oauth/oauth";
import { cookies } from "next/headers";
+import { RedirectError } from "@/utils/oauthLoginError";
export const GET = async (request: NextRequest) => {
const searchParams = request.nextUrl.searchParams;
@@ -82,15 +83,25 @@ export const GET = async (request: NextRequest) => {
};
const loginUser = async () => {
- const { data: loginResponse } = await axios.post(
- `${process.env.NEXT_PUBLIC_DOMAIN_URL}/api/oauth/login/${provider}`,
- googleUser.login
- );
- console.log("구글 로그인 성공:", loginResponse);
+ try {
+ const { data: loginResponse } = await axios.post(
+ `${process.env.NEXT_PUBLIC_DOMAIN_URL}/api/oauth/login/${provider}`,
+ googleUser.login
+ );
+ console.log("구글 로그인 성공:", loginResponse);
- // 쿠키 저장
- const { accessToken, refreshToken } = loginResponse;
- setCookies(accessToken, refreshToken);
+ // 쿠키 저장
+ const { accessToken, refreshToken } = loginResponse;
+ setCookies(accessToken, refreshToken);
+ } catch (error: any) {
+ if (error.response?.status === 403) {
+ console.log("회원가입이 필요합니다. 회원가입 페이지로 이동합니다...");
+ throw new RedirectError("/signup");
+ } else {
+ console.error("구글 로그인 중 오류:", error.message || error);
+ throw new Error("로그인 중 서버 오류");
+ }
+ }
};
const setCookies = (accessToken: string, refreshToken: string) => {
@@ -108,11 +119,18 @@ export const GET = async (request: NextRequest) => {
});
};
- await processUser();
+ try {
+ await processUser();
+ } catch (error) {
+ if (error instanceof RedirectError) {
+ return NextResponse.redirect(new URL(error.redirectPath, request.url));
+ }
+ throw error;
+ }
} catch (error: any) {
console.error("OAuth 처리 중 오류:", error.message || error);
return NextResponse.json({ message: error.message || "서버 오류" }, { status: 500 });
}
- return NextResponse.redirect(new URL("/", request.url));
+ return NextResponse.redirect(new URL("/mypage", request.url));
};
diff --git a/src/app/api/oauth/callback/kakao/route.ts b/src/app/api/oauth/callback/kakao/route.ts
index bc7a322a..50a4ba0d 100644
--- a/src/app/api/oauth/callback/kakao/route.ts
+++ b/src/app/api/oauth/callback/kakao/route.ts
@@ -3,6 +3,7 @@ import axios from "axios";
import apiClient from "@/lib/apiClient";
import { OauthLoginUser, OauthResponse, OauthSignupUser } from "@/types/oauth/oauth";
import { cookies } from "next/headers";
+import { RedirectError } from "@/utils/oauthLoginError";
export const GET = async (request: NextRequest) => {
const searchParams = request.nextUrl.searchParams;
@@ -71,8 +72,13 @@ export const GET = async (request: NextRequest) => {
const { accessToken, refreshToken } = loginResponse;
setCookies(accessToken, refreshToken);
} catch (error: any) {
- console.error("카카오 로그인 중 오류:", error.message || error);
- throw new Error("로그인 중 서버 오류");
+ if (error.response?.status === 403) {
+ console.log("회원가입이 필요합니다. 회원가입 시도 중...");
+ //회원가입 페이지로 리다이렉트
+ throw new RedirectError("/signup");
+ } else {
+ throw new Error("회원가입 중 서버 오류");
+ }
}
};
@@ -98,5 +104,5 @@ export const GET = async (request: NextRequest) => {
return NextResponse.json({ message: error.message || "서버 오류" }, { status: 500 });
}
- return NextResponse.redirect(new URL("/", request.url));
+ return NextResponse.redirect(new URL("/mypage", request.url));
};
diff --git a/src/hooks/queries/form/userFormDetail.ts b/src/hooks/queries/form/userFormDetail.ts
new file mode 100644
index 00000000..a1029129
--- /dev/null
+++ b/src/hooks/queries/form/userFormDetail.ts
@@ -0,0 +1,27 @@
+import { FormDetailResponse } from "@/types/response/form";
+import { useQuery } from "@tanstack/react-query";
+import axios from "axios";
+
+interface UseUserFormDetailParams {
+ formId?: number;
+}
+
+export const useUserFormDetail = ({ formId }: UseUserFormDetailParams) => {
+ const query = useQuery({
+ queryKey: ["formDetail", formId],
+ queryFn: async () => {
+ if (!formId) {
+ throw new Error("formId가 없습니다.");
+ }
+ const response = await axios.get(`/api/forms/${formId}`);
+ return response.data;
+ },
+ enabled: !!formId, // formId가 유효한 경우에만 쿼리를 실행
+ });
+
+ return {
+ ...query,
+ isPending: query.isPending,
+ error: query.error,
+ };
+};
diff --git a/src/utils/oauthLoginError.ts b/src/utils/oauthLoginError.ts
new file mode 100644
index 00000000..ac54f41d
--- /dev/null
+++ b/src/utils/oauthLoginError.ts
@@ -0,0 +1,10 @@
+export class RedirectError extends Error {
+ redirectPath: string;
+
+ constructor(redirectPath: string) {
+ super("Redirect required");
+ this.redirectPath = redirectPath;
+
+ Object.setPrototypeOf(this, RedirectError.prototype);
+ }
+}