Skip to content

Commit 8ae952e

Browse files
authored
S3 + CloutFront 배포를 위해 워크플로우를 작성합니다. (#51)
* feat: add deploy workflow * fix: fix deploy trigger branch to main
1 parent b4ef828 commit 8ae952e

File tree

7 files changed

+99
-54
lines changed

7 files changed

+99
-54
lines changed

.github/workflows/deploy.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Deploy React App to S3
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
deploy:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
# 1. 코드 체크아웃
14+
- name: Checkout code
15+
uses: actions/checkout@v3
16+
17+
# 2. Node.js 설치
18+
- name: Setup Node.js
19+
uses: actions/setup-node@v3
20+
with:
21+
node-version: '18'
22+
23+
# 3. 의존성 설치 (npm install)
24+
- name: Install dependencies
25+
run: npm install
26+
27+
# 4. React 앱 빌드
28+
- name: Build React app
29+
30+
run: |
31+
echo "VITE_API_BASE_URL=${{ secrets.VITE_API_BASE_URL }}" >> .env
32+
echo "VITE_USE_MSW=${{ secrets.VITE_USE_MSW }}" >> .env
33+
echo "VITE_KAKAO_MAP_KEY=${{ secrets.VITE_KAKAO_MAP_KEY }}" >> .env
34+
npm run build
35+
36+
# 5. AWS S3로 파일 업로드
37+
- name: Deploy to S3
38+
uses: awact/s3-action@master
39+
with:
40+
args: --acl public-read --follow-symlinks --delete
41+
env:
42+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
43+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
44+
AWS_REGION: 'ap-northeast-2'
45+
AWS_S3_BUCKET: ${{ secrets.S3_BUCKET_NAME }}
46+
SOURCE_DIR: './dist' # 빌드된 React 앱이 있는 디렉터리

src/pages/Reviews/components/Card.tsx

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
/** @jsxImportSource @emotion/react */
2-
import { css } from "@emotion/react";
2+
import { css } from '@emotion/react';
33

44
import colors from 'styles/color';
5-
import StarRating from 'pages/reviews/components/StarRating';
5+
import StarRating from 'pages/Reviews/components/StarRating';
66

77
import { hexToRgba } from 'utils/hexToRgba';
8-
import { CardProps } from './types';
8+
import { CardProps } from 'pages/Reviews/components/types';
99
import { getProfileImg } from 'utils/getProfileImg';
1010

11-
1211
const Card = ({ content, member, menus, reviewDate, grade }: CardProps) => {
1312
const { profileImage, nickname } = member;
1413
const { name } = menus;
@@ -36,27 +35,28 @@ const Card = ({ content, member, menus, reviewDate, grade }: CardProps) => {
3635
</div>
3736
</div>
3837
</div>
39-
)
40-
}
38+
);
39+
};
4140

4241
export default Card;
4342

44-
4543
const _cardContainer = css`
4644
border: 1px solid ${colors.icy};
4745
padding: 16px;
4846
margin: 16px 0;
4947
border-radius: 8px;
5048
background-color: ${colors.white};
51-
max-width: 100%; // 하단 가로 스크롤 방지
49+
max-width: 100%; // 하단 가로 스크롤 방지
5250
overflow: hidden;
5351
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
5452
55-
@media (max-width: 768px) { // 테블릿 대응
53+
@media (max-width: 768px) {
54+
// 테블릿 대응
5655
padding: 12px;
5756
}
5857
59-
@media (max-width: 480px) { // 모바일 대응
58+
@media (max-width: 480px) {
59+
// 모바일 대응
6060
padding: 8px;
6161
}
6262
`;
@@ -71,11 +71,11 @@ const _reviewContainer = css`
7171
display: flex;
7272
justify-content: space-between;
7373
align-items: center;
74-
flex-wrap: wrap; // 반응형을 위해 요소들을 줄 바꿈
75-
gap: 16px; // 간격을 설정하여 반응형 레이아웃에서의 간격 유지
74+
flex-wrap: wrap; // 반응형을 위해 요소들을 줄 바꿈
75+
gap: 16px; // 간격을 설정하여 반응형 레이아웃에서의 간격 유지
7676
7777
@media (max-width: 768px) {
78-
flex-direction: column; // 테블릿에서 세로 정렬
78+
flex-direction: column; // 테블릿에서 세로 정렬
7979
align-items: flex-start;
8080
}
8181
`;
@@ -86,7 +86,8 @@ const _profileContainer = css`
8686
align-items: flex-start;
8787
width: 85%;
8888
89-
@media (max-width: 480px) { // 모바일 대응
89+
@media (max-width: 480px) {
90+
// 모바일 대응
9091
gap: 12px;
9192
}
9293
`;
@@ -155,11 +156,11 @@ const _gradeContainer = css`
155156
width: 10%;
156157
157158
@media (max-width: 768px) {
158-
align-self: flex-end; // 테블릿에서 오른쪽 정렬
159+
align-self: flex-end; // 테블릿에서 오른쪽 정렬
159160
}
160161
161162
@media (max-width: 480px) {
162-
align-self: flex-start; // 모바일에서는 세로로 정렬
163+
align-self: flex-start; // 모바일에서는 세로로 정렬
163164
margin-top: 12px;
164165
}
165166
`;
@@ -174,5 +175,3 @@ const _grade = css`
174175
font-size: 1.2rem;
175176
}
176177
`;
177-
178-
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { render, renderHook, screen, waitFor } from '@testing-library/react';
2-
import CardList from 'pages/reviews/components/CardList';
2+
import CardList from 'pages/Reviews/components/CardList';
33
import { createWrapper } from 'tests/test-utils';
44

55
import { useGetStoresReview } from 'queries/modules/stores/useStoresQuery';
@@ -10,17 +10,19 @@ describe('CardList Components', () => {
1010
render(<CardList />, { wrapper: createWrapper() });
1111

1212
expect(screen.getByText(/ /i)).toBeInTheDocument();
13-
})
13+
});
1414

1515
it('useGetStoresReview API를 가져온 후 Card가 렌더링 되어야한다.', async () => {
1616
render(<CardList />, { wrapper: createWrapper() });
1717

18-
const { result } = renderHook(() => useGetStoresReview(1), { wrapper: createWrapper() });
18+
const { result } = renderHook(() => useGetStoresReview(1), {
19+
wrapper: createWrapper(),
20+
});
1921

2022
// useGetStoresReview 훅을 사용하여 API 호출 확인
2123
await waitFor(() => {
2224
expect(result.current.isSuccess).toBe(true);
23-
})
25+
});
2426

2527
// 데이터가 로드되고, Card 컴포넌트들이 렌더링되는지 확인
2628
await waitFor(() => {
@@ -30,5 +32,5 @@ describe('CardList Components', () => {
3032
expect(screen.getByText(review.content)).toBeInTheDocument();
3133
});
3234
});
33-
})
34-
})
35+
});
36+
});

src/pages/Reviews/components/CardList.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
/** @jsxImportSource @emotion/react */
2-
import { css } from "@emotion/react";
2+
import { css } from '@emotion/react';
33
import { useGetStoresReview } from 'queries/modules/stores/useStoresQuery';
4-
import Card from './Card';
4+
import Card from 'pages/Reviews/components/Card';
55

66
import get from 'lodash/get';
77
import isUndefined from 'lodash/isUndefined';
8-
import StarRating from 'pages/reviews/components/StarRating';
8+
import StarRating from 'pages/Reviews/components/StarRating';
99

1010
const CardList = () => {
1111
const { data: storeReviewData } = useGetStoresReview(1);
1212

13-
const reviews = get(storeReviewData, ['response', 'reviews']) ;
13+
const reviews = get(storeReviewData, ['response', 'reviews']);
1414
const grade = get(storeReviewData, ['response', 'grade']);
1515

1616
return (
@@ -19,10 +19,11 @@ const CardList = () => {
1919
<div css={_score}>내 음식점 리뷰 평균 평점</div>
2020
<StarRating rating={grade ?? 0} size={18} />
2121
</div>
22-
{isUndefined(reviews) ?
22+
{isUndefined(reviews) ? (
2323
<div css={__emptyReviewContainer}>
2424
<div css={_emptyReview}>리뷰가 없습니다.</div>
25-
</div> :
25+
</div>
26+
) : (
2627
<>
2728
{reviews?.map((review) => (
2829
<Card
@@ -36,10 +37,10 @@ const CardList = () => {
3637
/>
3738
))}
3839
</>
39-
}
40+
)}
4041
</div>
41-
)
42-
}
42+
);
43+
};
4344

4445
export default CardList;
4546

@@ -60,22 +61,22 @@ const _score = css`
6061
color: #333;
6162
6263
@media (max-width: 480px) {
63-
font-size: 1.0rem;
64+
font-size: 1rem;
6465
}
65-
`
66+
`;
6667

6768
const _emptyReview = css`
68-
font-size: 1.0rem;
69+
font-size: 1rem;
6970
color: #888;
7071
margin-top: 20px;
71-
`
72+
`;
7273

7374
const __emptyReviewContainer = css`
7475
display: flex;
7576
justify-content: center;
7677
align-items: center;
7778
height: 100px;
78-
font-size: 1.0rem;
79+
font-size: 1rem;
7980
color: #888;
8081
margin-top: 20px;
81-
`
82+
`;

src/pages/Reviews/components/StarRating.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
/** @jsxImportSource @emotion/react */
22
import { css } from '@emotion/react';
33
import { Star, StarEmpty } from 'common/components/icons';
4-
5-
import { StarRatingProps } from './types';
4+
import { StarRatingProps } from 'pages/Reviews/components/types';
65

76
const StarRating = ({ rating, size = 12 }: StarRatingProps) => {
87
const filledStars = Math.floor(rating); // 꽉 찬 별의 개수

src/pages/Reviews/index.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,30 @@
11
/** @jsxImportSource @emotion/react */
2-
import { css } from "@emotion/react";
2+
import { css } from '@emotion/react';
33
import { Button } from 'common/components';
44
import useAuth from 'common/hooks/useAuth';
55

6-
import CardList from 'pages/reviews/components/CardList';
6+
import CardList from 'pages/Reviews/components/CardList';
77

88
import colors from 'styles/color';
99

1010
const Reviews = () => {
11-
const { AuthGuard } = useAuth(["owner", "admin"]);
11+
const { AuthGuard } = useAuth(['owner', 'admin']);
1212

1313
return (
1414
<AuthGuard>
1515
<div css={[_container]}>
1616
<div>
17-
<div css={ _header}>
18-
<h2>
19-
Reviews
20-
</h2>
17+
<div css={_header}>
18+
<h2>Reviews</h2>
2119
<Button>Latest</Button>
22-
</div>
20+
</div>
2321
</div>
2422

2523
<CardList />
2624
</div>
2725
</AuthGuard>
28-
)
29-
}
26+
);
27+
};
3028

3129
export default Reviews;
3230

@@ -43,4 +41,4 @@ const _container = css`
4341
const _header = css`
4442
display: flex;
4543
justify-content: space-between;
46-
`;
44+
`;

src/routes/AppRoutes.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import MenuDetail from 'pages/menu/detail';
77
import Profile from 'pages/profile';
88
import Order from 'pages/order';
99
import OrderDetail from 'pages/orderDetail';
10-
import SignIn from 'pages/signIn';
11-
import Reviews from 'pages/reviews';
10+
import SignIn from 'pages/Signin';
11+
import Reviews from 'pages/Reviews';
1212
import Store from 'pages/store';
1313
import StoreDetail from 'pages/storeDetail';
1414
import User from 'pages/user';

0 commit comments

Comments
 (0)