-
Notifications
You must be signed in to change notification settings - Fork 13
[7주차] 러비 미션 제출합니다. #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
d1aee6a
fe77c41
d562ccc
881dab2
81e8733
3a0a3ff
67584a9
afe98f7
b69bc9f
8a5281e
da0c07c
363e900
d00ceef
8a79b50
e071658
5ba88a3
e9ce03e
28291fd
1912ec2
3c98b18
d1e6743
602e0e7
5e8f22a
9e7e170
cf6ed29
ec339ed
7028a89
d68d6df
f865070
79686eb
52f6aca
2b5e9b5
35f0185
07027b6
4130b74
ac9bd02
4b4f4e9
19eafa2
e6081b7
55aaf4e
4cdf753
22e9038
7c5ff2d
cc20234
01ce2a9
da41a69
78f854b
8eed898
73d2982
99e020c
6ac9357
b1f3b71
1353361
e793042
f3522aa
9653f1d
4b34fba
d954353
3228bf7
9ec5f91
3a4eabb
618117a
8570763
c6f057b
3fe0211
69cdd13
850c1d9
f554750
93d3fad
7b289b3
da8e050
e856fe8
75147de
9dade54
8425e95
0943eba
fa18ad2
197cbb9
b0579fd
302f057
0ca2d6e
a4d682d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| --- | ||
| name: template-issue | ||
| about: 이슈 템플릿 | ||
| title: 'design: 투표페이지 뷰' | ||
| labels: '' | ||
| assignees: '' | ||
|
|
||
| --- | ||
|
|
||
| ## 목적 | ||
|
|
||
| 설명 | ||
|
|
||
| ## 작업 상세 내용 | ||
|
|
||
| - [ ] todo1 | ||
| - [ ] todo2 | ||
| - [ ] todo3 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| ## Related Issues | ||
|
|
||
| - close #issue_number | ||
|
|
||
| ## PR 유형 | ||
|
|
||
| 어떤 변경 사항이 있나요? | ||
|
|
||
| - [ ] 새로운 기능 추가 | ||
| - [ ] 오류 수정 | ||
| - [ ] CSS 등 스타일 변경 | ||
| - [ ] 코드에 영향을 주지 않는 변경사항(오타 수정, 탭 사이즈 변경, 변수명 변경) | ||
| - [ ] 코드 리팩토링 | ||
| - [ ] 주석 추가 및 수정 | ||
| - [ ] 문서 수정 | ||
| - [ ] 테스트 추가, 테스트 리팩토링 | ||
| - [ ] 파일 혹은 폴더 삭제 | ||
|
|
||
| ## 변경 사항 in Detail | ||
|
|
||
| - [ ] 변경 사항 | ||
| - 변경 사항 상세 설명 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| module.exports = { | ||
| root: true, | ||
| env: { browser: true, es2020: true }, | ||
| extends: [ | ||
| "eslint:recommended", | ||
| "plugin:@typescript-eslint/recommended", | ||
| "plugin:react-hooks/recommended", | ||
| "some-other-config-you-use", | ||
| "prettier", | ||
| ], | ||
| ignorePatterns: ["dist", ".eslintrc.cjs"], | ||
| parser: "@typescript-eslint/parser", | ||
| plugins: ["prettier"], | ||
| rules: { | ||
| "prettier/prettier": "error", | ||
| }, | ||
| // plugins: ["react-refresh"], | ||
| // rules: { | ||
| // "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], | ||
| // }, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| # Logs | ||
| logs | ||
| *.log | ||
| npm-debug.log* | ||
| yarn-debug.log* | ||
| yarn-error.log* | ||
| pnpm-debug.log* | ||
| lerna-debug.log* | ||
|
|
||
| node_modules | ||
| dist | ||
| dist-ssr | ||
| *.local | ||
|
|
||
| # Editor directories and files | ||
| .vscode/* | ||
| !.vscode/extensions.json | ||
| .idea | ||
| .DS_Store | ||
| *.suo | ||
| *.ntvs* | ||
| *.njsproj | ||
| *.sln | ||
| *.sw? | ||
|
|
||
| .env |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "semi": true, | ||
| "tabWidth": 2, | ||
| "printWidth": 80, | ||
| "trailingComma": "all", | ||
| "bracketSameLine": true, | ||
| "endOfLine": "auto" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # Vite로 React 실행하기 | ||
|
|
||
| cd vote // Directory 이동 | ||
|
|
||
| code . // 해당 디렉토리를 최상위로 VSCode 실행 | ||
|
|
||
| yarn // 의존성 모듈 설치 | ||
|
|
||
| yarn dev // React 앱 실행 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <!doctype html> | ||
| <html lang="ko"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>리액트 투표페이지</title> | ||
| <link | ||
| rel="icon" | ||
| href="/src/assets/image/favicon.png" | ||
| type="image/svg+xml" /> | ||
| <link rel="preconnect" href="https://fonts.googleapis.com" /> | ||
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> | ||
| <link | ||
| href="https://fonts.googleapis.com/css2?family=Inter:[email protected]&display=swap" | ||
| rel="stylesheet" /> | ||
| </head> | ||
| <body> | ||
| <div id="root"></div> | ||
| <script type="module" src="/src/main.tsx"></script> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| { | ||
| "name": "vote", | ||
| "private": true, | ||
| "version": "0.0.0", | ||
| "type": "module", | ||
| "scripts": { | ||
| "dev": "vite", | ||
| "build": "tsc && vite build", | ||
| "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", | ||
| "preview": "vite preview" | ||
| }, | ||
| "dependencies": { | ||
| "@types/react-router-dom": "^5.3.3", | ||
| "axios": "^1.7.2", | ||
| "react": "^18.2.0", | ||
| "react-cookie": "^7.1.4", | ||
| "react-dom": "^18.2.0", | ||
| "react-query": "^3.39.3", | ||
| "react-router-dom": "^6.23.1", | ||
| "styled-components": "^6.1.11", | ||
| "styled-reset": "^4.5.2" | ||
|
Comment on lines
+20
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 스타일링을 위해서 styled-components 라이브러리를 사용하셨네요! 제가 지난 번에 전체 톡방에 올린 것처럼, reactJS 와 같이 CSR을 지원하는 라이브러리에서는 충분하지만 nextJS와 같이 서버 사이드 렌더링을 지원하는 경우 사용자가 네트워크가 느린 환경에서 애플리케이션을 이용할 경우 스타일링이 적용되지 않은 레이아웃만을 볼 가능성이 있어요! |
||
| }, | ||
| "devDependencies": { | ||
| "@types/node": "^20.12.12", | ||
| "@types/react": "^18.2.66", | ||
| "@types/react-dom": "^18.2.22", | ||
| "@types/styled-components": "^5.1.34", | ||
| "@typescript-eslint/eslint-plugin": "^7.2.0", | ||
| "@typescript-eslint/parser": "^7.2.0", | ||
| "@vitejs/plugin-react": "^4.2.1", | ||
| "eslint": "^8.57.0", | ||
| "eslint-config-prettier": "^9.1.0", | ||
| "eslint-plugin-prettier": "^5.1.3", | ||
| "eslint-plugin-react-hooks": "^4.6.0", | ||
| "eslint-plugin-react-refresh": "^0.4.6", | ||
| "prettier": "^3.2.5", | ||
| "typescript": "^5.2.2", | ||
| "vite": "^5.2.0", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. vite 번들러를 이용해서 리액트 프로젝트를 진행하셨네요. 기존의 cra은 내부적으로 webpack을 이용하여 번들링을 진행하는 반면에 vite는 esbuild라는 GO 언어를 통해 작성된 번들러를 개발환경에서, 배포환경에서는 rollup 을 이용해 HMR까지 지원하고 있다고 해요! |
||
| "vite-plugin-svgr": "^4.2.0", | ||
| "vite-tsconfig-paths": "^4.3.2" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { Suspense } from "react"; | ||
| import { Router } from "router"; | ||
| import { RouterProvider } from "react-router-dom"; | ||
| import { ThemeProvider } from "styled-components"; | ||
| import GlobalStyle from "@styles/globalStyle"; | ||
| import theme from "@styles/theme"; | ||
| import { QueryClient, QueryClientProvider } from "react-query"; | ||
|
|
||
| const queryClient = new QueryClient(); | ||
|
|
||
| function App() { | ||
| return ( | ||
| <QueryClientProvider client={queryClient}> | ||
| <Suspense fallback="..loading"> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suspense가 제대로 동작..하고 있는건가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 음... Suspense 컴포넌트를 이용해서 데이터를 로딩 중일 때 대체 UI를 보여주려면 fallback 속성에는 컴포넌트가 들어와야 하는 것 아닌가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그냥 "..loading" 텍스트가 뜨게끔 해두었습니다.. |
||
| <ThemeProvider theme={theme}> | ||
| <RouterProvider router={Router} /> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. router를 사용해 훨씬 깔끔한 코드가 된 거 같아요. |
||
| <GlobalStyle /> | ||
| </ThemeProvider> | ||
| </Suspense> | ||
| </QueryClientProvider> | ||
| ); | ||
| } | ||
|
|
||
| export default App; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { Cookies } from "react-cookie"; | ||
|
|
||
| const cookies = new Cookies(); | ||
|
|
||
| export function setCookie( | ||
| name: string, | ||
| value: string | undefined, | ||
| options: object, | ||
| ) { | ||
| return cookies.set(name, value, { ...options }); | ||
| } | ||
|
|
||
| export function getCookie(name: string) { | ||
| return cookies.get(name); | ||
| } | ||
|
|
||
| export function removeCookie(name: string) { | ||
| return cookies.remove(name); | ||
| } | ||
|
Comment on lines
+5
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cookie에 여러 값들을 저장하고, 조회하고, 삭제하는 기능의 함수들을 만들어주셨네요! 다만 이는 백엔드 단의 로직에서 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // import axios from "axios"; | ||
|
|
||
| // export const customAxios = axios.create({ | ||
| // baseURL: `${import.meta.env.VITE_APP_BASE_URL}`, | ||
| // headers: { | ||
| // "Content-Type": "application/json", | ||
| // "Access-Control-Allow-Origin": "*", | ||
| // }, | ||
| // }); | ||
|
|
||
| import axios, { AxiosInstance } from "axios"; | ||
|
|
||
| export const customAxios: AxiosInstance = axios.create({ | ||
| baseURL: `${import.meta.env.VITE_APP_BASE_URL}`, | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| "Access-Control-Allow-Origin": "*", | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 헤더는 preflight 요청 시에 본 요청에 대한 cors 이슈를 해결해주기 위해서 백엔드 단에서 진행하는 헤더 설정으로 알고 있어요! 프론트엔드의 출처(프로토콜 + 도메인 주소 + 포트번호)를 여기에 명시해주면, 해당 출처로부터의 요청은 브라우저가 막지 않게 되는 것이지요! 따라서 러비 팀에서 axios 인스턴스를 만들어 쓸때 필요한 설정이 아니라 백엔드에서 진행해야 하는 것으로 생각하는데, 이렇게 코드를 작성하신 이유가 따로 있나 궁금합니다. |
||
| }, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { AxiosResponse } from "axios"; | ||
| import { customAxios } from "./customAxios"; | ||
|
|
||
| export interface ResponseTypes { | ||
| votingOptionCount: number; | ||
| votingOptionName: string; | ||
| votingOptionId: number; | ||
| } | ||
|
|
||
| interface GetFinalResultTypes { | ||
| success: boolean; | ||
| response: ResponseTypes[]; | ||
| } | ||
|
|
||
| export async function getFinalResult( | ||
| topicID: number, | ||
| ): Promise<ResponseTypes[]> { | ||
| const { data }: AxiosResponse<GetFinalResultTypes> = await customAxios.get( | ||
| `/api/topics/${topicID}/results`, | ||
| ); | ||
|
|
||
| const { response } = data; | ||
| return response; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import { AxiosResponse } from "axios"; | ||
| import { customAxios } from "./customAxios"; | ||
|
|
||
| export interface VotingOptionDtoTypes { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| id: number; | ||
| name: string; | ||
| } | ||
|
|
||
| export interface ResponseTypes { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. getFinalResults.ts 파일에서도 동일한 이름의 인터페이스가 있는데, 좀더 semantic하게 작명하시면 좋을 것 같습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 한 파일에 하나의 api 함수만 존재한다면 여기서 type 정의 후 사용해도 괜찮을 거 같아요~ 다만 어떤 api들에 대한 type은 분리되어 있어서 파트너 간 정한 규칙으로 일관성을 유지했다면 더 좋을 거 같네요. 그리고 승완님 말씀처럼 타입을 export 해서 다른 파일에서도 사용한다면 일반적인 네이밍보다는 특수한 네이밍이 나을 거 같아요. GetTopicByIdRes 이런 식으로요~ |
||
| id: number; | ||
| name: string; | ||
| minimumVotesRequired: number; | ||
| votingOptionDto: VotingOptionDtoTypes[]; | ||
| } | ||
|
|
||
| interface GetTopicsByIdTypes { | ||
| success: boolean; | ||
| response: ResponseTypes; | ||
| } | ||
|
|
||
| export async function getTopicsById(topicID: number): Promise<ResponseTypes> { | ||
| const { data }: AxiosResponse<GetTopicsByIdTypes> = await customAxios.get( | ||
| `/api/topics/${topicID}`, | ||
| ); | ||
|
|
||
| const { response } = data; | ||
| return response; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import { AxiosResponse } from "axios"; | ||
| import { customAxios } from "./customAxios"; | ||
|
|
||
| export interface ResponseTypes { | ||
| id: number; | ||
| name: string; | ||
| voteCount: number; | ||
| } | ||
|
|
||
| interface GetVotingOptionsByIdTypes { | ||
| success: boolean; | ||
| response: ResponseTypes[]; | ||
| } | ||
|
|
||
| export async function getVotingOptionsById( | ||
| topicID: number, | ||
| ): Promise<ResponseTypes[]> { | ||
| const { data }: AxiosResponse<GetVotingOptionsByIdTypes> = | ||
| await customAxios.get(`/api/votingoptions/topics/${topicID}`); | ||
|
|
||
| const { response } = data; | ||
| return response; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { postEmailTypes } from "types/postEmailTypes"; | ||
| import { customAxios } from "./customAxios"; | ||
|
|
||
| export async function postEmail(props: postEmailTypes) { | ||
| const { email } = props; | ||
| const response = await customAxios.post("/api/users/verify", { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이메일 인증 방식까지 거치신 부분 너무 좋습니다 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 맞아요 최고입니다....👍 |
||
| email: email, | ||
| }); | ||
|
|
||
| return response; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import { postSignInTypes } from "types/postSignInTypes"; | ||
| import { customAxios } from "./customAxios"; | ||
| import { AxiosResponse } from "axios"; | ||
|
|
||
| export interface SignInResponse { | ||
| accessToken: string; | ||
| refreshToken: string; | ||
| message: string; | ||
| } | ||
|
|
||
| interface FullResponse { | ||
| success: boolean; | ||
| response: SignInResponse; | ||
| success_or_error_code: { | ||
| status: number; | ||
| message: string; | ||
| }; | ||
| } | ||
|
|
||
| export async function postSignIn( | ||
| props: postSignInTypes, | ||
| ): Promise<AxiosResponse<FullResponse>> { | ||
| const { username, password } = props; | ||
| const response = await customAxios.post<FullResponse>("/api/users/login", { | ||
| username, | ||
| password, | ||
| }); | ||
|
|
||
| return response; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import { postSignUpTypes } from "types/postSignUpTypes"; | ||
| import { customAxios } from "./customAxios"; | ||
|
|
||
| export async function postSignUp(props: postSignUpTypes) { | ||
| const { username, password, email, part, team, name, role } = props; | ||
| const response = await customAxios.post("/api/users/signup", { | ||
| username: username, | ||
| password: password, | ||
| email: email, | ||
| part: part, | ||
| team: team, | ||
| role: role, | ||
| name: name, | ||
| }); | ||
|
|
||
| return response; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import { postVoteTypes } from "types/postVoteTypes"; | ||
| import { customAxios } from "./customAxios"; | ||
|
|
||
| export async function postVote(props: postVoteTypes) { | ||
| const { topicId, votingOptionId } = props; | ||
| const response = await customAxios.post("/api/votes", { | ||
| topicId: topicId, | ||
| votingOptionId: votingOptionId, | ||
| }); | ||
|
|
||
| return response?.data.data; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
비트!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@songess 왜케 깜찍하게 말하세요