diff --git a/.yarnrc.yml b/.yarnrc.yml
new file mode 100644
index 00000000..3186f3f0
--- /dev/null
+++ b/.yarnrc.yml
@@ -0,0 +1 @@
+nodeLinker: node-modules
diff --git a/Dockerfile b/Dockerfile
index 512be29b..289592f6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
# 운영환경(main branch)에서 사용하는 Dockerfile
# 1단계: build
-FROM node:18-alpine AS builder
+FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
@@ -12,7 +12,7 @@ COPY .env .env
RUN yarn install && yarn build
# 2단계: production
-FROM node:18-alpine AS runner
+FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
diff --git a/Dockerfile.dev b/Dockerfile.dev
index d8e6b9b8..cc21afa1 100644
--- a/Dockerfile.dev
+++ b/Dockerfile.dev
@@ -1,7 +1,7 @@
# 운영환경(main branch)에서 사용하는 Dockerfile
# 1단계: build
-FROM node:18-alpine AS builder
+FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
@@ -12,7 +12,7 @@ COPY .env .env
RUN yarn install && yarn build
# 2단계: production
-FROM node:18-alpine AS runner
+FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
@@ -26,6 +26,9 @@ COPY --from=builder /app/next.config.ts ./next.config.ts
# 런타임시점에도 .env 파일 복사
COPY --from=builder /app/.env .env
+# 빌드 시 Node 힙 메모리를 4GB까지 허용 (4096 = 4GB)
+ENV NODE_OPTIONS="--max-old-space-size=4096"
+
# devDependencies는 설치하지 않고 dependencies만 설치
RUN yarn install --production
diff --git a/Dockerfile.prod b/Dockerfile.prod
index d8e6b9b8..cc21afa1 100644
--- a/Dockerfile.prod
+++ b/Dockerfile.prod
@@ -1,7 +1,7 @@
# 운영환경(main branch)에서 사용하는 Dockerfile
# 1단계: build
-FROM node:18-alpine AS builder
+FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
@@ -12,7 +12,7 @@ COPY .env .env
RUN yarn install && yarn build
# 2단계: production
-FROM node:18-alpine AS runner
+FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
@@ -26,6 +26,9 @@ COPY --from=builder /app/next.config.ts ./next.config.ts
# 런타임시점에도 .env 파일 복사
COPY --from=builder /app/.env .env
+# 빌드 시 Node 힙 메모리를 4GB까지 허용 (4096 = 4GB)
+ENV NODE_OPTIONS="--max-old-space-size=4096"
+
# devDependencies는 설치하지 않고 dependencies만 설치
RUN yarn install --production
diff --git a/app/(my)/my-study-review/page.tsx b/app/(my)/my-study-review/page.tsx
new file mode 100644
index 00000000..730daa9d
--- /dev/null
+++ b/app/(my)/my-study-review/page.tsx
@@ -0,0 +1,212 @@
+'use client';
+
+import Image from 'next/image';
+import { useState } from 'react';
+import KeywordReview from '@/entities/user/ui/keyword-review';
+import { MyReviewItem } from '@/features/study/api/types';
+import {
+ useMyNegativeKeywordsQuery,
+ useMyReviewsInfinityQuery,
+ useUserPositiveKeywordsQuery,
+} from '@/features/study/model/use-review-query';
+import { formatKoreaRelativeTime } from '@/shared/lib/time';
+
+export default function MyStudyReview() {
+ const { data: positiveKeywordsData } = useUserPositiveKeywordsQuery({
+ pageSize: 5,
+ });
+ const { data: negativeKeywordsData } = useMyNegativeKeywordsQuery({
+ pageSize: 5,
+ });
+ const {
+ data: myReviewsData,
+ fetchNextPage,
+ hasNextPage,
+ } = useMyReviewsInfinityQuery();
+
+ const positiveKeywords = positiveKeywordsData?.keywords || [];
+ const negativeKeywords = negativeKeywordsData?.keywords || [];
+
+ const positiveKeywordsCount = positiveKeywordsData?.totalCount || 0;
+ const negativeKeywordsCount = negativeKeywordsData?.totalCount || 0;
+
+ const totalKeywordsCount = positiveKeywordsCount + negativeKeywordsCount;
+
+ const myReviews = myReviewsData?.reviews || [];
+
+ return (
+ <>
+
+
+
+
받은 평가
+
+ {totalKeywordsCount}
+
+
+
+
+ 개선이 필요한 점은 나에게만 보여요
+
+
+
+
+
+
+
좋았던 점
+
+ {positiveKeywords.length > 5 && (
+
+ )}
+
+
+
+ {positiveKeywords.length > 0 ? (
+ positiveKeywords.map((keyword) => (
+
+ ))
+ ) : (
+
+ 아직 받은 평가가 없습니다.
+
+ )}
+
+
+
+
+
+
+ 개선이 필요한 점
+
+
+ {negativeKeywords.length > 5 && (
+
+ )}
+
+
+
+ {negativeKeywords.length > 0 ? (
+ negativeKeywords.map((keyword) => (
+
+ ))
+ ) : (
+
+ 아직 받은 평가가 없습니다.
+
+ )}
+
+
+
+
+
+
+
+
후기
+
+ {myReviewsData?.totalCount || 0}
+
+
+
+
+ 모든 후기는 나에게만 보여요
+
+
+
+ {myReviews.length > 0 ? (
+ myReviews.map((review) => )
+ ) : (
+
+ 아직까지 받은 후기가 없습니다.
+
+ )}
+
+ {hasNextPage && (
+
+ )}
+
+
+ >
+ );
+}
+
+function Review({ data }: { data: MyReviewItem }) {
+ const [expanded, setExpanded] = useState(false);
+
+ return (
+
+
+
+
+
+
+ {data.writer.memberName}
+
+ ·
+
+ {formatKoreaRelativeTime(data.reviewedAt)}
+
+
+
+
+
+
+ {data.content}
+
+
+
+
+
+
+ 스터디 기간
+
+ {data.startDate.replace(/-/g, '.')} ~{' '}
+ {data.endDate.replace(/-/g, '.')}
+
+
+
+ 스터디 주제
+
+ {data.studySubjects.join(', ')}
+
+
+
+
+ );
+}
diff --git a/app/global.css b/app/global.css
index 74119091..1cb1b7fa 100644
--- a/app/global.css
+++ b/app/global.css
@@ -192,6 +192,10 @@ https://velog.io/@oneook/tailwindcss-4.0-%EB%AC%B4%EC%97%87%EC%9D%B4-%EB%8B%AC%E
--color-background-brand-default: var(--color-rose-500);
--color-background-brand-strong: var(--color-rose-700);
+ --color-background-neutral-subtle: var(--color-gray-200);
+ --color-background-neutral-default: var(--color-gray-500);
+ --color-background-neutral-strong: var(--color-gray-900);
+
--color-background-accent-blue-subtle: var(--color-blue-50);
--color-background-accent-blue-default: var(--color-blue-100);
--color-background-accent-blue-strong: var(--color-blue-600);
@@ -226,6 +230,10 @@ https://velog.io/@oneook/tailwindcss-4.0-%EB%AC%B4%EC%97%87%EC%9D%B4-%EB%8B%AC%E
--color-background-accent-yellow-default: var(--color-yellow-100);
--color-background-accent-yellow-strong: var(--color-yellow-600);
+ --color-background-success-subtle: var(--color-green-300);
+ --color-background-success-default: var(--color-green-500);
+ --color-background-success-strong: var(--color-green-700);
+
--color-fill-brand-default-default: var(--color-rose-500);
--color-fill-brand-default-hover: var(--color-rose-600);
--color-fill-brand-default-pressed: var(--color-rose-700);
diff --git a/next.config.ts b/next.config.ts
index e1dcedae..108ea078 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -15,6 +15,11 @@ const nextConfig: NextConfig = {
hostname: 'test-api.zeroone.it.kr',
pathname: '/profile-image/**',
},
+ {
+ protocol: 'https',
+ hostname: 'api.zeroone.it.kr',
+ pathname: '/profile-image/**',
+ },
{
protocol: 'https',
hostname: 'lh3.googleusercontent.com',
diff --git a/package.json b/package.json
index 76b2ac30..4334a658 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
"api:logs": "docker-compose -f ../study-platform-mvp/docker-compose.yml logs -f mvp-app"
},
"dependencies": {
+ "@hookform/resolvers": "^5.2.1",
"@next/third-parties": "^15.3.3",
"@radix-ui/react-avatar": "^1.1.9",
"@radix-ui/react-dialog": "^1.1.10",
@@ -37,10 +38,12 @@
"react": "^19.0.0",
"react-day-picker": "9.4.3",
"react-dom": "^19.0.0",
+ "react-hook-form": "^7.62.0",
"tailwind-merge": "^3.2.0",
"tailwindcss": "^4.0.6",
"tailwindcss-animate": "^1.0.7",
"tw-animate-css": "^1.2.5",
+ "zod": "^4.0.17",
"zustand": "^5.0.3"
},
"devDependencies": {
diff --git a/public/apply-study.svg b/public/apply-study.svg
new file mode 100644
index 00000000..36d1f6ac
--- /dev/null
+++ b/public/apply-study.svg
@@ -0,0 +1,9 @@
+
diff --git a/public/feedback.svg b/public/feedback.svg
new file mode 100644
index 00000000..60d352ad
--- /dev/null
+++ b/public/feedback.svg
@@ -0,0 +1,9 @@
+
diff --git a/public/icons/arrow-down.svg b/public/icons/arrow-down.svg
new file mode 100644
index 00000000..10f6a659
--- /dev/null
+++ b/public/icons/arrow-down.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/fine-review.svg b/public/icons/fine-review.svg
new file mode 100644
index 00000000..13307185
--- /dev/null
+++ b/public/icons/fine-review.svg
@@ -0,0 +1,9 @@
+
diff --git a/public/icons/good-review.svg b/public/icons/good-review.svg
new file mode 100644
index 00000000..e4b9aeeb
--- /dev/null
+++ b/public/icons/good-review.svg
@@ -0,0 +1,9 @@
+
diff --git a/public/icons/shame-review.svg b/public/icons/shame-review.svg
new file mode 100644
index 00000000..50ee650e
--- /dev/null
+++ b/public/icons/shame-review.svg
@@ -0,0 +1,9 @@
+
diff --git a/public/icons/shape.svg b/public/icons/shape.svg
new file mode 100644
index 00000000..af8ad1d5
--- /dev/null
+++ b/public/icons/shape.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/images/start-study.png b/public/images/start-study.png
deleted file mode 100644
index 52b6682a..00000000
Binary files a/public/images/start-study.png and /dev/null differ
diff --git a/public/profile-default.svg b/src/entities/user/ui/icon/profile-default.svg
similarity index 74%
rename from public/profile-default.svg
rename to src/entities/user/ui/icon/profile-default.svg
index 04ddfe2c..5e77401f 100644
--- a/public/profile-default.svg
+++ b/src/entities/user/ui/icon/profile-default.svg
@@ -1,12 +1,12 @@
-