Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…de-zap into refactor/969-set-monorepo
  • Loading branch information
healim01 committed Jan 13, 2025
2 parents 29b1a3d + ecf28c6 commit 5df7aec
Show file tree
Hide file tree
Showing 65 changed files with 247 additions and 131 deletions.
75 changes: 75 additions & 0 deletions .github/workflows/frontend_cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Frontend CD

on:
push:
branches:
- main
- dev/fe
paths:
- "frontend/**"

jobs:
deploy:
runs-on: ubuntu-latest
env:
frontend-directory: ./frontend
steps:
- name: 체크아웃
uses: actions/checkout@v4

- name: Node.js 설치
uses: actions/setup-node@v4
with:
node-version: "20"

- name: 환경 파일 생성
run: |
if [ "${{ github.ref_name }}" == "main" ]; then
echo "REACT_APP_API_URL=${{ secrets.REACT_APP_API_URL }}" > ${{ env.frontend-directory }}/.env.production
echo "APP_ENV=${{ secrets.APP_ENV_PROD }}" >> ${{ env.frontend-directory }}/.env.production
else
echo "REACT_APP_API_URL=${{ secrets.REACT_APP_BETA_API_URL }}" > ${{ env.frontend-directory }}/.env.production
echo "APP_ENV=${{ secrets.APP_ENV_DEV }}" >> ${{ env.frontend-directory }}/.env.production
fi
echo "SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> ${{ env.frontend-directory }}/.env.production
echo "SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}" >> ${{ env.frontend-directory }}/.env.sentry-build-plugin
echo "AMPLITUDE_API_KEY=${{ secrets.AMPLITUDE_API_KEY }}" >> ${{ env.frontend-directory }}/.env.production
- name: 환경 파일 권한 설정
run: chmod 644 ${{ env.frontend-directory }}/.env.*

- name: npm install
run: npm install
working-directory: ${{ env.frontend-directory }}

- name: npm run build
run: npm run build
working-directory: ${{ env.frontend-directory }}

- name: AWS credentials 설정
uses: aws-actions/configure-aws-credentials@v3
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2

- name: S3 업로드
run: |
if [ "${{ github.ref_name }}" == "main" ]; then
aws s3 sync ./dist s3://${{ secrets.S3_BUCKET_NAME }}/prod/ --delete
else
aws s3 sync ./dist s3://${{ secrets.S3_BUCKET_NAME }}/dev/ --delete
fi
working-directory: ${{ env.frontend-directory }}

- name: CloudFront 캐시 무효화
run: |
if [ "${{ github.ref_name }}" == "main" ]; then
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID_PROD }} \
--paths "/*"
else
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID_DEV }} \
--paths "/*"
fi
39 changes: 9 additions & 30 deletions design-system/ContactUs/ContactUs.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
import { useState } from 'react';

import {
Button,
Input,
LoadingBall,
Modal,
Text,
Textarea,
} from '@/components';
import { Button, Input, LoadingBall, Modal, Text, Textarea } from '@/components';
import { useInput, useInputWithValidate, useToggle, useToast } from '@/hooks';
import { useAuth } from '@/hooks/authentication';
import { validateEmail } from '@/service/validates';
import { theme } from '@design/style/theme';
import { theme } from '@/style/theme';

import * as S from './ContactUs.style';

Expand All @@ -34,9 +27,7 @@ const ContactUs = () => {

const isValidContents = message.trim().length !== 0;

const handleSubmit = (
e: React.FormEvent<HTMLFormElement> | React.MouseEvent<HTMLButtonElement>
) => {
const handleSubmit = (e: React.FormEvent<HTMLFormElement> | React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();

if (!isValidContents || emailErrorMessage) {
Expand Down Expand Up @@ -94,15 +85,11 @@ const ContactUs = () => {
<Modal.Body>
<S.Form onSubmit={handleSubmit}>
<Text.Medium as='p' color={theme.color.light.secondary_500}>
질문/피드백을 편하게 남겨주세요! 여러분의 의견은 더 나은 서비스를
만드는 데 큰 도움이 됩니다. <br />
이미지 등을 함께 보내실 경우 [email protected]으로 직접
이메일을 보내실 수 있습니다.
질문/피드백을 편하게 남겨주세요! 여러분의 의견은 더 나은 서비스를 만드는 데 큰 도움이 됩니다. <br />
이미지 등을 함께 보내실 경우 [email protected]으로 직접 이메일을 보내실 수 있습니다.
</Text.Medium>
<Textarea id='voc' variant='outlined'>
<Textarea.Label htmlFor={'voc'}>
무엇을 도와드릴까요?
</Textarea.Label>
<Textarea.Label htmlFor={'voc'}>무엇을 도와드릴까요?</Textarea.Label>
<Textarea.TextField
minRows={5}
maxRows={10}
Expand All @@ -112,16 +99,11 @@ const ContactUs = () => {
/>
</Textarea>
<Text.Medium as='p' color={theme.color.light.secondary_500}>
답변이 필요하시면 아래 이메일 주소를 남겨주세요. 이메일은 오직
답변을 위해서만 사용됩니다 :)
답변이 필요하시면 아래 이메일 주소를 남겨주세요. 이메일은 오직 답변을 위해서만 사용됩니다 :)
</Text.Medium>
<Input variant='outlined' isValid={!emailErrorMessage}>
<Input.Label>이메일 (선택)</Input.Label>
<Input.TextField
value={email}
onChange={handleEmail}
disabled={isSending}
/>
<Input.TextField value={email} onChange={handleEmail} disabled={isSending} />
<Input.HelperText>{emailErrorMessage}</Input.HelperText>
</Input>
</S.Form>
Expand All @@ -135,10 +117,7 @@ const ContactUs = () => {
<LoadingBall />
</S.LoadingContainer>
) : (
<Button
disabled={isValidContents && !emailErrorMessage ? false : true}
onClick={handleSubmit}
>
<Button disabled={isValidContents && !emailErrorMessage ? false : true} onClick={handleSubmit}>
보내기
</Button>
)}
Expand Down
6 changes: 3 additions & 3 deletions design-system/PagingButtons/PagingButtons.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Text } from '@/components';
import { trackMyTemplatePaging } from '@/service/amplitude/track';
import { theme } from '@design/style/theme';
import { trackMemberTemplatePaging } from '@/service/amplitude/track';
import { theme } from '@/style/theme';

import * as S from './PagingButtons.style';

Expand Down Expand Up @@ -29,7 +29,7 @@ const PagingButtons = ({
};

const handlePagingClick = (page: number, label: string) => {
trackMyTemplatePaging({ page, label });
trackMemberTemplatePaging({ page, label });
onPageChange(page);
};

Expand Down
14 changes: 2 additions & 12 deletions design-system/TemplateCard/TemplateCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,7 @@ interface Props {
}

const TemplateCard = ({ template }: Props) => {
const {
title,
description,
thumbnail,
tags,
modifiedAt,
member,
visibility,
} = template;
const { title, description, thumbnail, tags, createdAt, member, visibility } = template;
const [showAllTagList, toggleShowAllTagList] = useToggle();
const isPrivate = visibility === VISIBILITY_PRIVATE;

Expand Down Expand Up @@ -77,9 +69,7 @@ const TemplateCard = ({ template }: Props) => {
color={theme.color.light.secondary_600}
/>
<S.NoWrapTextWrapper>
<Text.Small color={theme.color.light.secondary_600}>
{formatRelativeTime(modifiedAt)}
</Text.Small>
<Text.Small color={theme.color.light.secondary_600}>{formatRelativeTime(createdAt)}</Text.Small>
</S.NoWrapTextWrapper>
</S.TimeContainer>
</Flex>
Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"test": "jest",
"e2e": "playwright test",
"e2e:ui": "playwright test --ui",
"msw": "APP_ENV=msw webpack-dev-server --open --config webpack.dev.js",
"dev": "webpack-dev-server --config webpack.dev.js --open",
"tsc": "tsc --noEmit",
"build": "webpack --mode production --config webpack.prod.js",
Expand Down
15 changes: 12 additions & 3 deletions frontend/src/api/ApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ export class ApiClient {
this.credentials = credentials || 'same-origin';
}

private getAuthorizationHeader(): HeadersInit {
const token = localStorage.getItem('authorization');
return token ? { Authorization: token } : {};
}

async get(endpoint: string, params?: RequestParams): Promise<Response> {
const url = new URL(`${this.baseUrl}${endpoint}`);

Expand All @@ -63,7 +68,10 @@ export class ApiClient {
try {
const response = await fetch(url, {
method,
headers: this.headers,
headers: {
...this.headers,
...this.getAuthorizationHeader(),
},
credentials: this.credentials,
body: body ? JSON.stringify(body) : null,
});
Expand All @@ -83,13 +91,14 @@ export class ApiClient {
}

private async handleError(response: Response) {
const { errorCode, instance, detail } = await response.json();

if (response.status === HTTP_STATUS.UNAUTHORIZED) {
localStorage.removeItem('authorization');
localStorage.removeItem('name');
localStorage.removeItem('memberId');
}

const { errorCode, instance, detail } = await response.json();

throw new ApiError(getErrorMessage(errorCode, instance), response.status, errorCode, detail);
}
}
6 changes: 1 addition & 5 deletions frontend/src/api/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ import type { LoginRequest, SignupRequest } from '@/types';

export const postSignup = async (signupInfo: SignupRequest) => await apiClient.post(END_POINTS.SIGNUP, signupInfo);

export const postLogin = async (loginInfo: LoginRequest) => {
const response = await apiClient.post(END_POINTS.LOGIN, loginInfo);

return await response.json();
};
export const postLogin = async (loginInfo: LoginRequest) => await apiClient.post(END_POINTS.LOGIN, loginInfo);

export const postLogout = async () => await apiClient.post(END_POINTS.LOGOUT, {});

Expand Down
5 changes: 2 additions & 3 deletions frontend/src/api/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ export const API_URL = process.env.REACT_APP_API_URL ?? 'https://default-url.com

const httpHeader = {
'Content-Type': 'application/json',
};
const httpCredentials = 'include';
}

export const apiClient = new ApiClient(API_URL, httpHeader, httpCredentials);
export const apiClient = new ApiClient(API_URL, httpHeader);
13 changes: 13 additions & 0 deletions frontend/src/api/contact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { END_POINTS } from '@/routes';

import { apiClient } from './config';

export interface ContactBody {
message: string;
email: string | null;
name: string | null;
memberId: number | null;
}

export const postContact = async (contactBody: ContactBody) =>
await apiClient.post(`${END_POINTS.CONTACT}`, contactBody);
3 changes: 0 additions & 3 deletions frontend/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
export { API_URL } from './config';
export { QUERY_KEY } from './queryKeys';
export {
PAGE_SIZE,
SORTING_OPTIONS,
DEFAULT_SORTING_OPTION,
getTemplateList,
getTemplateExplore,
getTemplate,
Expand Down
21 changes: 1 addition & 20 deletions frontend/src/api/templates.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,7 @@
import { apiClient } from '@/api/config';
import { DEFAULT_SORTING_OPTION, PAGE_SIZE } from '@/models/templates';
import { END_POINTS } from '@/routes';
import type { TemplateRequest, TemplateEditRequest, TemplateUploadRequest, TemplateListRequest } from '@/types';
import { SortingOption } from '@/types';

export const PAGE_SIZE = 20;

export const SORTING_OPTIONS: SortingOption[] = [
{
key: 'modifiedAt,desc',
value: '최근 순',
},
{
key: 'modifiedAt,asc',
value: '오래된 순',
},
{
key: 'likesCount,desc',
value: '좋아요 순',
},
];

export const DEFAULT_SORTING_OPTION = SORTING_OPTIONS[0];

export const getTemplateList = async ({
keyword,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks/useQueryParams.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCallback } from 'react';
import { useSearchParams } from 'react-router-dom';

import { DEFAULT_SORTING_OPTION } from '@/api';
import { DEFAULT_SORTING_OPTION } from '@/models/templates';

interface FilterState {
category: number;
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ Sentry.init({
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);

const enableMocking = async () => {
if (process.env.NODE_ENV !== 'development') {
if (process.env.APP_ENV !== 'msw') {
return;
}

// const { worker } = await import('./mocks/browser');
const { worker } = await import('@/mocks/settings/browser');

// await worker.start();
await worker.start();
};

enableMocking().then(() => {
Expand Down
3 changes: 0 additions & 3 deletions frontend/src/mocks/handlers/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ export const authenticationHandler = [
http.post(`${API_URL}${END_POINTS.LOGOUT}`, () =>
mockResponse({
status: 204,
body: {
statusText: 'AUTHORIZED',
},
}),
),

Expand Down
4 changes: 3 additions & 1 deletion frontend/src/mocks/handlers/category.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { http } from 'msw';

import { API_URL } from '@/api';
import { categories as mockCategoryList } from '@/mocks/fixtures/categoryList.json';
import categories from '@/mocks/fixtures/categoryList.json';
import { END_POINTS } from '@/routes';
import { Category } from '@/types';
import { mockResponse } from '@/utils/mockResponse';

const mockCategoryList = [...categories.categories];

export const categoryHandlers = [
http.get(`${API_URL}${END_POINTS.CATEGORIES}`, () =>
mockResponse({ status: 200, body: { categories: mockCategoryList } }),
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/mocks/handlers/contact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { http } from 'msw';

import { API_URL } from '@/api';
import { END_POINTS } from '@/routes';
import { mockResponse } from '@/utils/mockResponse';

export const contactHandlers = [http.get(`${API_URL}${END_POINTS.CONTACT}`, () => mockResponse({ status: 201 }))];
Loading

0 comments on commit 5df7aec

Please sign in to comment.