diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..6fe45e5 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,47 @@ +name: Deploy React App to S3 + +on: + push: + branches: + - main + - feature/* + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + # 1. 코드 체크아웃 + - name: Checkout code + uses: actions/checkout@v3 + + # 2. Node.js 설치 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: '18' + + # 3. 의존성 설치 (npm install) + - name: Install dependencies + run: npm install + + # 4. React 앱 빌드 + - name: Build React app + + run: | + echo "VITE_API_BASE_URL=${{ secrets.VITE_API_BASE_URL }}" >> .env + echo "VITE_USE_MSW=${{ secrets.VITE_USE_MSW }}" >> .env + echo "VITE_KAKAO_MAP_KEY=${{ secrets.VITE_KAKAO_MAP_KEY }}" >> .env + npm run build + + # 5. AWS S3로 파일 업로드 + - name: Deploy to S3 + uses: awact/s3-action@master + with: + args: --acl public-read --follow-symlinks --delete + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: 'ap-northeast-2' + AWS_S3_BUCKET: ${{ secrets.S3_BUCKET_NAME }} + SOURCE_DIR: './dist' # 빌드된 React 앱이 있는 디렉터리 diff --git a/src/pages/Reviews/components/Card.tsx b/src/pages/Reviews/components/Card.tsx index 90674bb..a28d723 100644 --- a/src/pages/Reviews/components/Card.tsx +++ b/src/pages/Reviews/components/Card.tsx @@ -1,14 +1,13 @@ /** @jsxImportSource @emotion/react */ -import { css } from "@emotion/react"; +import { css } from '@emotion/react'; import colors from 'styles/color'; -import StarRating from 'pages/reviews/components/StarRating'; +import StarRating from 'pages/Reviews/components/StarRating'; import { hexToRgba } from 'utils/hexToRgba'; -import { CardProps } from './types'; +import { CardProps } from 'pages/Reviews/components/types'; import { getProfileImg } from 'utils/getProfileImg'; - const Card = ({ content, member, menus, reviewDate, grade }: CardProps) => { const { profileImage, nickname } = member; const { name } = menus; @@ -36,27 +35,28 @@ const Card = ({ content, member, menus, reviewDate, grade }: CardProps) => { - ) -} + ); +}; export default Card; - const _cardContainer = css` border: 1px solid ${colors.icy}; padding: 16px; margin: 16px 0; border-radius: 8px; background-color: ${colors.white}; - max-width: 100%; // 하단 가로 스크롤 방지 + max-width: 100%; // 하단 가로 스크롤 방지 overflow: hidden; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1); - @media (max-width: 768px) { // 테블릿 대응 + @media (max-width: 768px) { + // 테블릿 대응 padding: 12px; } - @media (max-width: 480px) { // 모바일 대응 + @media (max-width: 480px) { + // 모바일 대응 padding: 8px; } `; @@ -71,11 +71,11 @@ const _reviewContainer = css` display: flex; justify-content: space-between; align-items: center; - flex-wrap: wrap; // 반응형을 위해 요소들을 줄 바꿈 - gap: 16px; // 간격을 설정하여 반응형 레이아웃에서의 간격 유지 + flex-wrap: wrap; // 반응형을 위해 요소들을 줄 바꿈 + gap: 16px; // 간격을 설정하여 반응형 레이아웃에서의 간격 유지 @media (max-width: 768px) { - flex-direction: column; // 테블릿에서 세로 정렬 + flex-direction: column; // 테블릿에서 세로 정렬 align-items: flex-start; } `; @@ -86,7 +86,8 @@ const _profileContainer = css` align-items: flex-start; width: 85%; - @media (max-width: 480px) { // 모바일 대응 + @media (max-width: 480px) { + // 모바일 대응 gap: 12px; } `; @@ -155,11 +156,11 @@ const _gradeContainer = css` width: 10%; @media (max-width: 768px) { - align-self: flex-end; // 테블릿에서 오른쪽 정렬 + align-self: flex-end; // 테블릿에서 오른쪽 정렬 } @media (max-width: 480px) { - align-self: flex-start; // 모바일에서는 세로로 정렬 + align-self: flex-start; // 모바일에서는 세로로 정렬 margin-top: 12px; } `; @@ -174,5 +175,3 @@ const _grade = css` font-size: 1.2rem; } `; - - diff --git a/src/pages/Reviews/components/CardList.test.tsx b/src/pages/Reviews/components/CardList.test.tsx index 20906ee..3357f97 100644 --- a/src/pages/Reviews/components/CardList.test.tsx +++ b/src/pages/Reviews/components/CardList.test.tsx @@ -1,5 +1,5 @@ import { render, renderHook, screen, waitFor } from '@testing-library/react'; -import CardList from 'pages/reviews/components/CardList'; +import CardList from 'pages/Reviews/components/CardList'; import { createWrapper } from 'tests/test-utils'; import { useGetStoresReview } from 'queries/modules/stores/useStoresQuery'; @@ -10,17 +10,19 @@ describe('CardList Components', () => { render(, { wrapper: createWrapper() }); expect(screen.getByText(/내 음식점 리뷰 평균 평점/i)).toBeInTheDocument(); - }) + }); it('useGetStoresReview API를 가져온 후 Card가 렌더링 되어야한다.', async () => { render(, { wrapper: createWrapper() }); - const { result } = renderHook(() => useGetStoresReview(1), { wrapper: createWrapper() }); + const { result } = renderHook(() => useGetStoresReview(1), { + wrapper: createWrapper(), + }); // useGetStoresReview 훅을 사용하여 API 호출 확인 await waitFor(() => { expect(result.current.isSuccess).toBe(true); - }) + }); // 데이터가 로드되고, Card 컴포넌트들이 렌더링되는지 확인 await waitFor(() => { @@ -30,5 +32,5 @@ describe('CardList Components', () => { expect(screen.getByText(review.content)).toBeInTheDocument(); }); }); - }) -}) + }); +}); diff --git a/src/pages/Reviews/components/CardList.tsx b/src/pages/Reviews/components/CardList.tsx index 13d736b..a29cb97 100644 --- a/src/pages/Reviews/components/CardList.tsx +++ b/src/pages/Reviews/components/CardList.tsx @@ -1,16 +1,16 @@ /** @jsxImportSource @emotion/react */ -import { css } from "@emotion/react"; +import { css } from '@emotion/react'; import { useGetStoresReview } from 'queries/modules/stores/useStoresQuery'; -import Card from './Card'; +import Card from 'pages/Reviews/components/Card'; import get from 'lodash/get'; import isUndefined from 'lodash/isUndefined'; -import StarRating from 'pages/reviews/components/StarRating'; +import StarRating from 'pages/Reviews/components/StarRating'; const CardList = () => { const { data: storeReviewData } = useGetStoresReview(1); - const reviews = get(storeReviewData, ['response', 'reviews']) ; + const reviews = get(storeReviewData, ['response', 'reviews']); const grade = get(storeReviewData, ['response', 'grade']); return ( @@ -19,10 +19,11 @@ const CardList = () => {
내 음식점 리뷰 평균 평점
- {isUndefined(reviews) ? + {isUndefined(reviews) ? (
리뷰가 없습니다.
-
: + + ) : ( <> {reviews?.map((review) => ( { /> ))} - } + )} - ) -} + ); +}; export default CardList; @@ -60,22 +61,22 @@ const _score = css` color: #333; @media (max-width: 480px) { - font-size: 1.0rem; + font-size: 1rem; } -` +`; const _emptyReview = css` - font-size: 1.0rem; + font-size: 1rem; color: #888; margin-top: 20px; -` +`; const __emptyReviewContainer = css` display: flex; justify-content: center; align-items: center; height: 100px; - font-size: 1.0rem; + font-size: 1rem; color: #888; margin-top: 20px; -` \ No newline at end of file +`; diff --git a/src/pages/Reviews/components/StarRating.tsx b/src/pages/Reviews/components/StarRating.tsx index 6a847b7..fe08f2d 100644 --- a/src/pages/Reviews/components/StarRating.tsx +++ b/src/pages/Reviews/components/StarRating.tsx @@ -1,8 +1,7 @@ /** @jsxImportSource @emotion/react */ import { css } from '@emotion/react'; import { Star, StarEmpty } from 'common/components/icons'; - -import { StarRatingProps } from './types'; +import { StarRatingProps } from 'pages/Reviews/components/types'; const StarRating = ({ rating, size = 12 }: StarRatingProps) => { const filledStars = Math.floor(rating); // 꽉 찬 별의 개수 diff --git a/src/pages/Reviews/index.tsx b/src/pages/Reviews/index.tsx index 1e85c55..9e33108 100644 --- a/src/pages/Reviews/index.tsx +++ b/src/pages/Reviews/index.tsx @@ -1,32 +1,30 @@ /** @jsxImportSource @emotion/react */ -import { css } from "@emotion/react"; +import { css } from '@emotion/react'; import { Button } from 'common/components'; import useAuth from 'common/hooks/useAuth'; -import CardList from 'pages/reviews/components/CardList'; +import CardList from 'pages/Reviews/components/CardList'; import colors from 'styles/color'; const Reviews = () => { - const { AuthGuard } = useAuth(["owner", "admin"]); + const { AuthGuard } = useAuth(['owner', 'admin']); return (
-
-

- Reviews -

+
+

Reviews

-
+
- ) -} + ); +}; export default Reviews; @@ -43,4 +41,4 @@ const _container = css` const _header = css` display: flex; justify-content: space-between; -`; \ No newline at end of file +`; diff --git a/src/routes/AppRoutes.tsx b/src/routes/AppRoutes.tsx index a69f228..f9810f7 100644 --- a/src/routes/AppRoutes.tsx +++ b/src/routes/AppRoutes.tsx @@ -7,8 +7,8 @@ import MenuDetail from 'pages/menu/detail'; import Profile from 'pages/profile'; import Order from 'pages/order'; import OrderDetail from 'pages/orderDetail'; -import SignIn from 'pages/signIn'; -import Reviews from 'pages/reviews'; +import SignIn from 'pages/Signin'; +import Reviews from 'pages/Reviews'; import Store from 'pages/store'; import StoreDetail from 'pages/storeDetail'; import User from 'pages/user';