Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion week08/wantkdd/mission3/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ dist-ssr
*.sln
*.sw?

.env
.env
.vercel
4 changes: 2 additions & 2 deletions week08/wantkdd/mission3/src/apis/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ export const deleteUser = async (): Promise<void> => {

export const getUserMe = async (): Promise<User> => {
const response = await PrivateAPI.get<ApiResponse<User>>('/users/me');
return response.data;
return response.data.data;
};

export const updateUser = async (
data: UpdateUserRequest
): Promise<User> => {
const response = await PrivateAPI.patch<ApiResponse<User>>('/users', data);
return response.data;
return response.data.data;
};
2 changes: 1 addition & 1 deletion week08/wantkdd/mission3/src/apis/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const getLpComments = async ({
params: { cursor, limit, order },
}
);
return response.data;
return response.data.data;
};

// λŒ“κΈ€ μž‘μ„±
Expand Down
6 changes: 3 additions & 3 deletions week08/wantkdd/mission3/src/apis/lp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ export const getLpsByTag = async (params: GetLpsByTagParams): Promise<LpPaginati

export const getLpDetail = async (lpId: number): Promise<Lp> => {
const response = await PrivateAPI.get<LpDetailResponse>(`/lps/${lpId}`);
return response.data;
return response.data.data;
};

export const createLp = async (lpData: CreateLpRequest): Promise<Lp> => {
const response = await PrivateAPI.post<CreateLpResponse>('/lps', lpData);
return response.data;
return response.data.data;
};

export const deleteLp = async (lpId: number): Promise<void> => {
Expand All @@ -40,7 +40,7 @@ export const deleteLp = async (lpId: number): Promise<void> => {

export const updateLp = async (lpId: number, lpData: UpdateLpRequest): Promise<Lp> => {
const response = await PrivateAPI.patch<UpdateLpResponse>(`/lps/${lpId}`, lpData);
return response.data;
return response.data.data;
};

// lp μ’‹μ•„μš”
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useCallback, memo } from 'react';
import Button from '../button/Button';

interface OrderSelectorProps {
Expand All @@ -9,24 +10,32 @@ const OrderSelector = ({
order = 'desc',
onOrderChange,
}: OrderSelectorProps) => {
const handleDescClick = useCallback(() => {
onOrderChange('desc');
}, [onOrderChange]);

const handleAscClick = useCallback(() => {
onOrderChange('asc');
}, [onOrderChange]);

return (
<div className="flex justify-end gap-2 mb-4">
<Button
variant={order === 'desc' ? 'primary' : 'secondary'}
size="sm"
onClick={() => onOrderChange('desc')}
onClick={handleDescClick}
>
μ΅œμ‹ μˆœ
</Button>
<Button
variant={order === 'asc' ? 'primary' : 'secondary'}
size="sm"
onClick={() => onOrderChange('asc')}
onClick={handleAscClick}
>
였래된순
</Button>
</div>
);
};

export default OrderSelector;
export default memo(OrderSelector);
8 changes: 6 additions & 2 deletions week08/wantkdd/mission3/src/pages/home/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect, useRef } from 'react';
import { useState, useEffect, useRef, useCallback } from 'react';
import { useLps } from '../../hooks/lp/useLps';
import type { GetLpsParams, Lp } from '../../types/lp';
import Error from '../../components/error/Error';
Expand All @@ -9,6 +9,10 @@ import OrderSelector from '../../components/orderSelector/OrderSelector';

const HomePage = () => {
const [order, setOrder] = useState<GetLpsParams['order']>('desc');

const handleOrderChange = useCallback((newOrder: 'asc' | 'desc') => {
setOrder(newOrder);
}, []);
const {
data,
fetchNextPage,
Expand Down Expand Up @@ -67,7 +71,7 @@ const HomePage = () => {

return (
<div className="p-4">
<OrderSelector order={order} onOrderChange={setOrder} />
<OrderSelector order={order} onOrderChange={handleOrderChange} />
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
{showSkeleton
? Array.from({ length: 20 }).map((_, index) => (
Expand Down
8 changes: 5 additions & 3 deletions week08/wantkdd/mission3/src/pages/home/components/LpCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useNavigate } from 'react-router-dom';
import { useEffect, useRef, useState } from 'react';
import { useEffect, useRef, useState, useCallback, memo } from 'react';
import type { Lp } from '../../../types/lp';
import Skeleton from '../../../components/skeleton/Skeleton';
import clsx from 'clsx';
Expand Down Expand Up @@ -31,7 +31,9 @@ const LpCard = ({ lp }: LpCardProps) => {
};
}, [lp.thumbnail]);

const handleClick = () => navigate(`/lp/${lp.id}`);
const handleClick = useCallback(() => {
navigate(`/lp/${lp.id}`);
}, [navigate, lp.id]);

return (
<div
Expand All @@ -55,4 +57,4 @@ const LpCard = ({ lp }: LpCardProps) => {
);
};

export default LpCard;
export default memo(LpCard);
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useState, useCallback, memo } from 'react';
import { User } from 'lucide-react';
import type { Comment } from '../../../../types/comment';
import { useAuthStore } from '../../../../store/authStore';
Expand All @@ -23,15 +23,15 @@ const CommentItem = ({ comment, lpId }: CommentItemProps) => {

const isAuthor = user?.id === comment.authorId;

const handleEdit = () => {
const handleEdit = useCallback(() => {
setIsEditing(true);
};
}, []);

const handleCancelEdit = () => {
const handleCancelEdit = useCallback(() => {
setIsEditing(false);
};
}, []);

const handleSaveEdit = (content: string) => {
const handleSaveEdit = useCallback((content: string) => {
updateCommentMutation(
{
lpId,
Expand All @@ -44,14 +44,14 @@ const CommentItem = ({ comment, lpId }: CommentItemProps) => {
},
}
);
};
}, [updateCommentMutation, lpId, comment.id]);

const handleDelete = () => {
const handleDelete = useCallback(() => {
deleteCommentMutation({
lpId,
commentId: comment.id,
});
};
}, [deleteCommentMutation, lpId, comment.id]);

return (
<div className="flex gap-3 lg:gap-4 py-4 border-b border-gray-700">
Expand Down Expand Up @@ -93,4 +93,4 @@ const CommentItem = ({ comment, lpId }: CommentItemProps) => {
);
};

export default CommentItem;
export default memo(CommentItem);
25 changes: 25 additions & 0 deletions week10/wantkdd/mission1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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?
.vercel
73 changes: 73 additions & 0 deletions week10/wantkdd/mission1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# 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) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).

## Expanding the ESLint configuration

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

```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...

// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,

// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:

```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
23 changes: 23 additions & 0 deletions week10/wantkdd/mission1/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs['recommended-latest'],
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])
13 changes: 13 additions & 0 deletions week10/wantkdd/mission1/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<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>mission1</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
35 changes: 35 additions & 0 deletions week10/wantkdd/mission1/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "mission1",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@tailwindcss/vite": "^4.1.13",
"axios": "^1.12.2",
"lucide-react": "^0.544.0",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-router-dom": "^7.9.2",
"react-spinners": "^0.17.0",
"tailwindcss": "^4.1.13"
},
"devDependencies": {
"@eslint/js": "^9.36.0",
"@types/react": "^19.1.13",
"@types/react-dom": "^19.1.9",
"@vitejs/plugin-react": "^5.0.3",
"eslint": "^9.36.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.4.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.44.0",
"vite": "^7.1.7"
}
}
Loading
Loading