Skip to content
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

[4주차 기본/심화/공유 과제] API 통신 #6

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b9fad10
init: 필요없는 파일 삭제
seueooo Nov 10, 2024
ad30e16
style: theme, style 설정
seueooo Nov 10, 2024
cf5d5a7
style: login페이지 스타일 설정
seueooo Nov 10, 2024
962d526
feat: 회원가입 퍼널 구현
seueooo Nov 10, 2024
f6acbd4
feat: mypage 탭 구현
seueooo Nov 10, 2024
65021b0
feat: mypage 퍼블리싱 수정
seueooo Nov 10, 2024
0ac10cd
feat: button 공통컴포넌트 생성 및 적용
seueooo Nov 10, 2024
8c2d14a
feat: api 훅 생성
seueooo Nov 11, 2024
037ed77
feat: 회원가입 구현
seueooo Nov 11, 2024
fee5234
fix: 회원가입 input 비활성화 오류 해결
seueooo Nov 11, 2024
32fc85c
feat: 나의 취미 get 연결
seueooo Nov 11, 2024
bfe7236
feat: 로그아웃 구현
seueooo Nov 11, 2024
afb0c12
feat: 마이페이지 취미 검색 구현
seueooo Nov 11, 2024
4c4de71
feat: 내정보 수정 기능 구현
seueooo Nov 11, 2024
a00c08a
feat: 비밀번호 확인 로직 구현
seueooo Nov 11, 2024
96bb3fe
feat: 오류 alert 처리
seueooo Nov 11, 2024
b633c39
fix: alert 처리 수정
seueooo Nov 11, 2024
48af8cb
feat: 회원가입 에러메세지 구현
seueooo Nov 11, 2024
c7a315c
feat: 에러메세지 코드 수정
seueooo Nov 11, 2024
c7a0921
feat: 비밀번호 보이기 버튼 구현 완료
seueooo Nov 11, 2024
b9744b8
style: 폰트 적용
seueooo Nov 11, 2024
deefd42
fix: 회원가입 오류 해결
seueooo Nov 11, 2024
571e9df
refactor: 쓸모없는 코드 삭제
seueooo Nov 14, 2024
fa70bc4
refactor: 스타일 수정 및 handler함수 수정
seueooo Nov 14, 2024
3546329
fix: 로그인 페이지 에러 처리 수정
seueooo Nov 14, 2024
5925bf6
fix: 에러처리 수정
seueooo Nov 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}
1 change: 1 addition & 0 deletions week4_assignment/my-project/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_BASE_URL = "http://211.188.53.75:8080"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3: 옹 .env가 올라와있는데 혹시 과제 채점때문에 함께 올려주신건가요?! gitignore 하시지 않은 이유가 궁금합시당

27 changes: 27 additions & 0 deletions week4_assignment/my-project/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2: 오홍 위에 엔브가 올라와있다고 코리 남겼는데, .env 가 있는데 적용이 잘 되지 않았던 것 같습니다!
저도 예전에 깃이그노어에서 걸러내려고 했는데 엔브가 올라갔던 경험이 있었어서 그때부터 의식적으로 엔브를 올리지 않기 위해 한 방법이 있는데요! 공유해보겠습니다 :) (전 무려 구글 로그인 구현하려다가 구글키 올려버린.. )

1️⃣ gitignore에 .env 관련 내용 적어주기

.env
.env.local
.env.production
.env.development

2️⃣ git add . 시 항상 git status 찍어보고 .env 포함되었는지 확인하기
만약 포함되어있다면 git rm --cached .env 해주기


50 changes: 50 additions & 0 deletions week4_assignment/my-project/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:

- Configure the top-level `parserOptions` property like this:

```js
export default tseslint.config({
languageOptions: {
// other options...
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
},
})
```

- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
- Optionally add `...tseslint.configs.stylisticTypeChecked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:

```js
// eslint.config.js
import react from 'eslint-plugin-react'

export default tseslint.config({
// Set the react version
settings: { react: { version: '18.3' } },
plugins: {
// Add the react plugin
react,
},
rules: {
// other rules...
// Enable its recommended rules
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
},
})
```
28 changes: 28 additions & 0 deletions week4_assignment/my-project/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)
13 changes: 13 additions & 0 deletions week4_assignment/my-project/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p4: HTML의 언어 속성 부분 사소한 부분이지만 저두 맨날 ko로 바꿔주는거 까먹게 되더라구요!

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
33 changes: 33 additions & 0 deletions week4_assignment/my-project/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "my-project",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "^11.13.3",
"axios": "^1.7.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.3.0",
"react-router-dom": "^6.28.0"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react-swc": "^3.5.0",
"eslint": "^9.13.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.14",
"globals": "^15.11.0",
"typescript": "~5.6.2",
"typescript-eslint": "^8.11.0",
"vite": "^5.4.10"
}
}
1 change: 1 addition & 0 deletions week4_assignment/my-project/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions week4_assignment/my-project/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createBrowserRouter, RouterProvider } from "react-router-dom";

import LoginPage from "./pages/loginPage/LoginPage";
import SignUpPage from "./pages/signupPage/SignUpPage";
import MyPage from "./pages/myPage/MyPage";

const router = createBrowserRouter([
{
path: "/",
element: <LoginPage></LoginPage>,
},
{
path: "/signup",
element: <SignUpPage></SignUpPage>,
},
{
path: "/mypage",
element: <MyPage></MyPage>,
Comment on lines +9 to +18

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2 : 컴포넌트에 children이 없을 경우 self-closing 하면 좋을 것 같습니다!

},
]);

function App() {
return (
<>
<RouterProvider router={router}></RouterProvider>
</>
);
}

export default App;
68 changes: 68 additions & 0 deletions week4_assignment/my-project/src/components/MyInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/** @jsxImportSource @emotion/react */
import { mypageInput } from "../pages/myPage/MyPage.style";
import Button from "./common/Button/Button";
import updateUserData from "../libs/apis/updateUserData";
import { useState } from "react";
import { useNavigate } from "react-router-dom";

const MyInfo = () => {
const [input, setInput] = useState({
password: "",
hobby: "",
});
const navigate = useNavigate();
const userToken = localStorage.getItem("userToken");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3: tsx 파일에서 토큰을 가져오는 것과, axios 쏠때 토큰을 가져오는 것중 어디에서 받아오는게 좋을지 고민을 했었는데요.
저의 경우에는 매개변수로 넘겨주는 것보다, 로직 처리할때 호출되면 get해서 가져오는걸로 코드를 작성했습니다! ui 를 보여주는 곳보다는 api 호출때에 가져오는게 더 좋겠다고 생각해서 그랬는데요
요 코드를 보니 한서님의 생각도 너무너무 궁금합니다!!

Copy link

@suwonthugger suwonthugger Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p5: 토큰 처리는 axios instance 만들어서 사용하면 재사용성을 높일 수 있을것 같아요.
아래 예시 코드 첨부드립니다!

const authClient = axios.create(authAxiosConfig);

authClient.interceptors.request.use(
	async (config) => {
		const accessToken = getAuthHeader();
		if (accessToken) {
			config.headers.Authorization = `Bearer ${accessToken}`;
		} else {
			console.error('토큰이 없습니다. 로그인이 필요합니다.');
			window.location.replace('/auth/login');
		}
		return config;
	},
	(error) => {
		console.error(error);
	},
);

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setInput((prev) => ({
...prev,
[name]: value,
}));
};

const handleSubmit = () => {
if (userToken && (input.password || input.hobby)) {
updateUserData({
hobby: input.hobby,
password: input.password,
userToken: userToken,
});
alert("수정에 성공했습니다!");
navigate("/");
} else {
alert("칸을 입력해주세요.");
}
};

return (
<>
<h1>내 정보 수정하기</h1>
<h2>새 비밀번호</h2>
<div className="search_sec">
<input
name="password"
value={input.password}
css={mypageInput}
type="password"
onChange={handleInputChange}
/>
</div>

<h2>새 취미</h2>
<div className="search_sec">
<input
onChange={handleInputChange}
name="hobby"
value={input.hobby}
css={mypageInput}
type="text"
/>
<Button type="button" onClick={handleSubmit}>
수정하기
</Button>
</div>
</>
);
};

export default MyInfo;
50 changes: 50 additions & 0 deletions week4_assignment/my-project/src/components/SearchHobby.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/** @jsxImportSource @emotion/react */
import { mypageInput } from "../pages/myPage/MyPage.style";
import Button from "./common/Button/Button";
import useGetMyHobby from "../libs/apis/useGetMyHobby";
import useGetUserHobby from "../libs/apis/useGetUserHobby";
import { hobbyPstyle } from "../pages/myPage/MyPage.style";
import { useState } from "react";

const SearchHobby = () => {
const [userNo, setUserNo] = useState(0);
const [hobby, setHobby] = useState("");

const userToken = localStorage.getItem("userToken");

const { myHobby } = useGetMyHobby(userToken);
const { userHobby } = useGetUserHobby({ userToken, userNo });

const handleClickSearch = () => {
if (userHobby) {
setHobby(userHobby);
}
};

return (
<>
<h1>취미</h1>
<h2>나의 취미</h2>
<p css={hobbyPstyle}>{myHobby}</p>
<h2>다른 사람들의 취미</h2>
<div className="search_sec">
<input
css={mypageInput}
type="text"
placeholder="사용자 번호"
onChange={(e) => setUserNo(Number(e.target.value))}
/>
<Button type="button" onClick={handleClickSearch}>
검색
</Button>
</div>
{hobby && (
<p css={hobbyPstyle}>
{userNo}번 사용자의 취미: {hobby}
</p>
)}
</>
);
};

export default SearchHobby;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { css } from "@emotion/react";
import { Theme } from "../../../styles/theme";

export const buttonStyle = css`
width: 100%;
padding: 0.7rem 1rem;
margin-bottom: 0.5rem;
color: ${Theme.color.white};
background-color: ${Theme.color.lightGray_1};
border: 0px solid;
border-radius: 5px;
cursor: pointer;
:hover {
background-color: ${Theme.color.lightGray_3};
transition: all 1s;
}
&:disabled {
background-color: #cccccc;
cursor: not-allowed;
opacity: 0.5;
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/** @jsxImportSource @emotion/react */
import { ButtonHTMLAttributes, ReactNode } from "react";
import { buttonStyle } from "./Button.style";

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
children: ReactNode;
}
Comment on lines +5 to +7

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3 : { ...props }로 받아오면 이렇게 type을 지정할 수 있군요!! 대박.....!!!!!!
저도 children 관련해서 ReactNode로 했다가 PropsWithChildren 이라는 타입을 찾아서 관련된 고민을 한 것을 pr에 작성했는데, 한번 읽어보셔도 좋을 것 같습니다 :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p5: 왕 .. 전 진짜 이번 과제에서 버튼 분리할 생각도 못했는데 진짜 한서님 코드 보고 저 정말 깨달음을 얻었어요!!!


const Button = ({ children, disabled, ...props }: ButtonProps) => {
return (
<button {...props} css={buttonStyle} disabled={disabled}>
{children}
</button>
);
};
Comment on lines +9 to +15

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3 : 와우!! 이게 pr에 작성해주신 코드네요!! 저는 공통 컴포넌트 구현할 때 props를 주는 것마다 하나하나 작성을 해줬는데 속성으로 지정할 내용을 그대로 전달해서 바로 { ...props }로 적용하면 너무 좋을 것 같아요!! 지식 배워갑니다 :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{... props} 세미나에서도 배웠는데, 이렇게 쓰이니 진짜 공용 컴포넌트 줄때 완전 확장성이 배로 느네요! 진짜 코드 보면서 많이 배웁니다


export default Button;
Loading