FSD ์ํคํ ์ฒ์ ํจ๊ป ์๊ฐํด๋ณด๊ธฐ
Vue3์์ React.js๋ก ํ๋ก์ ํธ๋ฅผ ๋ง์ด๊ทธ๋ ์ด์ ํ๋ฉด์ Feature-Sliced Design(FSD) ์ํคํ ์ฒ๋ฅผ ๋์ ํ๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค. ๊ทธ ๊ณผ์ ์์ ๋ฐฐ๋ด ํ์ผ(barrel files)์ ๋ํ ๊ณ ๋ฏผ์ด ์๊ฒผ๊ณ , ์ต๊ทผ ๋ช ๋ ์ฌ์ด์ ์ด์ ๋ํ ๊ด์ ์ด ๋ง์ด ๋ฐ๋์๋ค๋ ๊ฒ์ ๋ฐ๊ฒฌํ์ต๋๋ค.
๋ฐฐ๋ด ํ์ผ์ด๋?
๋ฐฐ๋ด ํ์ผ์ ์ฌ๋ฌ ๋ชจ๋์ ํ๋์ ํ์ผ(์ฃผ๋ก index.js ๋๋ index.ts)์์ ๋ค์ ๋ด๋ณด๋ด๋ ํจํด์ ๋๋ค. ์ด ํจํด์ ์ฝ๋ ์ ๋ฆฌ์ import ๋ฌธ์ ๋จ์ํํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
๋ฐฐ๋ด ํ์ผ ์์ด:
// ๊ฐ ํ์ผ์์ ๊ฐ๋ณ์ ์ผ๋ก ๊ฐ์ ธ์ค๊ธฐ
import { Button } from "../components/Button";
import { TextField } from "../components/TextField";
import { Checkbox } from "../components/Checkbox";
๋ฐฐ๋ด ํ์ผ ์ฌ์ฉ ์:
// ํ๋์ ๊ฒฝ๋ก์์ ๋ชจ๋ ์ปดํฌ๋ํธ ๊ฐ์ ธ์ค๊ธฐ
import { Button, TextField, Checkbox } from "../components";
๋ฐฐ๋ด ํ์ผ์ ์ค์ ๊ตฌํ
์ผ๋ฐ์ ์ธ ๋ฐฐ๋ด ํ์ผ (components/index.ts):
// ์์ผ๋์นด๋ ๋ด๋ณด๋ด๊ธฐ (๊ถ์ฅํ์ง ์์)
export * from './Button';
export * from './TextField';
export * from './Checkbox';
// ๋๋ ๋ช
์์ ๋ด๋ณด๋ด๊ธฐ (๊ถ์ฅ)
export { Button } from './Button';
export { TextField } from './TextField';
export { Checkbox } from './Checkbox';
๋ฐฐ๋ด ํ์ผ์ ์ฑ๋ฅ ๋ฌธ์
Marvin์ JavaScript ์ํ๊ณ ์๋ ๊ฐ์ : ๋ฐฐ๋ด ํ์ผ ๋ฌธ์ ๊ธ์์ ์ง์ ํ ๋ฐ์ ๊ฐ์ด, ๋ฐฐ๋ด ํ์ผ์ ๋ค์๊ณผ ๊ฐ์ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์์ต๋๋ค:
- ๋ชจ๋ ๊ทธ๋ํ ๋ณต์ก์ฑ ์ฆ๊ฐ: ๋ฐฐ๋ด ํ์ผ์ด ์ฆ๊ฐํ ์๋ก ๋ชจ๋ ๊ฐ ์์กด์ฑ ๊ทธ๋ํ๊ฐ ๋ณต์กํด์ง๋๋ค.
- ๋ถํ์ํ ๋ชจ๋ ๋ก๋ฉ: ํนํ ์์ผ๋์นด๋ ๋ด๋ณด๋ด๊ธฐ(export * from)๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ, ์ค์ ๋ก ํ์ํ์ง ์์ ๋ชจ๋๊น์ง ๋ก๋๋ ์ ์์ต๋๋ค.
๋ชจ๋ ๊ทธ๋ํ ๋น๊ต
๋ฐฐ๋ด ํ์ผ ์์ด:
App.js
โโโ Button.js
โโโ TextField.js
โโโ Checkbox.js
๋ฐฐ๋ด ํ์ผ ์ฌ์ฉ ์:
App.js
โโโ components/index.js
โโโ Button.js
โโโ TextField.js
โโโ Checkbox.js
์ฑ๋ฅ ํ ์คํธ ๊ฒฐ๊ณผ ์์
๋ชจ๋ ์๋ฐฐ๋ด ํ์ผ ์์๋ฐฐ๋ด ํ์ผ ์ฌ์ฉ์ฐจ์ด
๋ชจ๋ ์ | ๋ฐฐ๋ด ํ์ผ ์์ | ๋ฐฐ๋ด ํ์ผ ์ฌ์ฉ | ์ฐจ์ด |
10๊ฐ | 150ms | 180ms | +20% |
50๊ฐ | 450ms | 630ms | +40% |
100๊ฐ | 820ms | 1250ms | +52% |
์ฐธ๊ณ : ์ด ์์น๋ ์์์ฉ์ด๋ฉฐ, ์ค์ ํ๋ก์ ํธ์์๋ ๋ค์ํ ์์ธ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ์ ์์ต๋๋ค.
FSD ์ํคํ ์ฒ์์์ ๋ฐฐ๋ด ํ์ผ
Feature-Sliced Design(FSD)์ ๋ฐฐ๋ด ํ์ผ๊ณผ ์ ์ฌํ ํจํด์ ์๋์ ์ผ๋ก ํ์ฉํฉ๋๋ค. ์ด๋ ๋จ์ํ ์ฝ๋ ์์ฑ์ ํธ์์ฑ์ ์ํ ๊ฒ์ด ์๋๋ผ, ์ํคํ
์ฒ์ ๊ตฌ์กฐ์ ๋ชฉ์ ์ ๋ฐ์ํ ๊ฒฐ์ ์
๋๋ค.
FSD๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ธฐ๋ฅ ๋จ์๋ก ๋ถํ ํ๊ณ ๊ณ์ธตํ๋ ๊ตฌ์กฐ๋ฅผ ๋ง๋๋ ๊ฒ์ ๋ชฉํ๋ก ํฉ๋๋ค. ์ด ๊ตฌ์กฐ์์ ๊ฐ ๋ ์ด์ด(layer)์ ์ฌ๋ผ์ด์ค(slice)๋ ๋ช ํํ ์ฑ ์๊ณผ ์ญํ ์ ๊ฐ์ง๋๋ค. ๋ฐฐ๋ด ํ์ผ๊ณผ ์ ์ฌํ ํจํด์ ์ฌ์ฉํจ์ผ๋ก์จ, FSD๋ ๋ค์๊ณผ ๊ฐ์ ์ด์ ์ ์ป์ต๋๋ค:
- ์บก์ํ: ๊ฐ ๋ชจ๋์ ๋ด๋ถ ๊ตฌํ์ ์จ๊ธฐ๊ณ ๊ณต๊ฐ API๋ง์ ๋ ธ์ถํฉ๋๋ค.
- ์์กด์ฑ ๊ด๋ฆฌ: ์์ ๋ ์ด์ด๊ฐ ํ์ ๋ ์ด์ด์๋ง ์ ๊ทผํ ์ ์๋๋ก ์ ํํฉ๋๋ค.
- ์ฌ์ฌ์ฉ์ฑ: ๊ณตํต ๊ธฐ๋ฅ์ ์ฝ๊ฒ ๊ณต์ ํ๊ณ ์ฌ์ฌ์ฉํ ์ ์๊ฒ ํฉ๋๋ค.
- ์ ์ง๋ณด์์ฑ: ์ฝ๋ ๊ตฌ์กฐ๋ฅผ ์ผ๊ด๋๊ฒ ์ ์งํ์ฌ ๊ฐ๋ฐ์๊ฐ ์ฝ๊ฒ ์ดํดํ๊ณ ์์ ํ ์ ์๊ฒ ํฉ๋๋ค.
์ด๋ฌํ ๊ตฌ์กฐ๋ ๋๊ท๋ชจ ํ๋ก์ ํธ์์ ์ฝ๋์ ์กฐ์งํ์ ๊ด๋ฆฌ๋ฅผ ์ฉ์ดํ๊ฒ ๋ง๋ค์ด, ์ฅ๊ธฐ์ ์ผ๋ก ํ๋ก์ ํธ์ ํ์ฅ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ํฅ์์ํต๋๋ค. ๋ฐ๋ผ์ FSD์์ ๋ฐฐ๋ด ํ์ผ๊ณผ ์ ์ฌํ ํจํด์ ์ฌ์ฉ์ ๋จ์ํ ํธ์์ฑ์ ๋์ด์, ์ํคํ ์ฒ์ ํต์ฌ ์์น์ ์คํํ๊ธฐ ์ํ ์ ๋ต์ ์ ํ์ด๋ผ๊ณ ๋ณผ ์ ์์ต๋๋ค.
FSD์์์ ๋ฐฐ๋ด ํ์ผ ํ์ฉ - ์ค์ ๊ฒฝํ๊ณผ ํ
FSD ์ํคํ ์ฒ๋ฅผ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณธ ๊ฒฝํ์ ๋ฐ๋ฅด๋ฉด, ๋ฐฐ๋ด ํ์ผ(Public API)์ ์ฃผ์ ์ด์ ์ค ํ๋๋ ์ ์ง๋ณด์์ฑ ํฅ์์ ๋๋ค. ์ด์ ๋ํด ์ด๋์ํด ์ฌ์ง์ ๊ณผ์ ์์ ๋ง๋ ๋๋ฃ ๊ฐ๋ฐ์๋ถ๊ป์ ๋ค์๊ณผ ๊ฐ์ ์ธ์ฌ์ดํธ๋ฅผ ๊ณต์ ํด์ฃผ์ จ์ด์:
"FSD๋ฅผ ์ฌ์ฉํ๋ฉด์ ๋๋ ๋ฐ๋ก๋, Public API๋ฅผ ํ์ฉํ๋ ์ฃผ๋ ์ด์ ์ค ํ๋๊ฐ ์ ์ง๋ณด์์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, entities/user์ ์คํค๋ง๊ฐ ์ผ๋ถ ๋ณ๊ฒฝ๋๋๋ผ๋ features/loginUser, features/registerUser ๋ฑ์์ import ๊ตฌ๋ฌธ์ ๋ณ๊ฒฝํ ํ์๊ฐ ์์ด์ง๋๋ค."
์ด๋ FSD์ ํต์ฌ ์์น ์ค ํ๋์ธ ๋ชจ๋ ๊ฐ ์์กด์ฑ ๊ด๋ฆฌ์ ์ผ์นํ๋ ๊ด์ ์
๋๋ค.
๋ฐฐ๋ด ํ์ผ ์์ฑ ์๋ํ
๋๊ท๋ชจ ํ๋ก์ ํธ์์ ๋งค๋ฒ ์๋์ผ๋ก ๋ฐฐ๋ด ํ์ผ(Public API)์ ๊ด๋ฆฌํ๋ ๊ฒ์ ๋ฒ๊ฑฐ๋ก์ธ ์ ์์ต๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ์ค์ฉ์ ์ธ ์ ๊ทผ ๋ฐฉ๋ฒ์ผ๋ก, ๋ฐฐ๋ด ํ์ผ์ ์๋์ผ๋ก ์์ฑํ๋ ์คํฌ๋ฆฝํธ๋ฅผ ํ์ฉํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด:
pnpm api features/loginUser
์ด๋ฌํ CLI ๋ช
๋ น์ด๋ฅผ ํตํด features/loginUser ๋ด๋ถ์ ๋ชจ๋์ ์๋์ผ๋ก ๋ถ์ํ๊ณ , ๋ช
์์ ์ผ๋ก ๋ด๋ณด๋ด๋ ๋ฐฐ๋ด ํ์ผ์ ์์ฑํ ์ ์์ต๋๋ค. ์ด๋ _๋ก ์์ํ๋ ํ์ผ์ด๋ ํด๋๋ ๋ด๋ถ์ฉ์ผ๋ก ๊ฐ์ฃผํ์ฌ ์๋์ผ๋ก ์ ์ธํ ์ ์์ต๋๋ค.
๋ช ์์ ๋ด๋ณด๋ด๊ธฐ์ ์ค์์ฑ
์์ ์ธ๊ธํ๋ฏ์ด, ์์ผ๋์นด๋ ๋ด๋ณด๋ด๊ธฐ(export * from './module')๋ ๊ถ์ฅ๋์ง ์์ต๋๋ค. ๋์ , ๋ช ์์ ๋ด๋ณด๋ด๊ธฐ๋ฅผ ์ฌ์ฉํ๋ฉด ์ฑ๋ฅ ์ด์๋ฅผ ์ต์ํํ๋ฉด์๋ FSD์ ์ด์ ์ ์ถฉ๋ถํ ํ์ฉํ ์ ์์ต๋๋ค.
// Good: ๋ช
์์ ๋ด๋ณด๋ด๊ธฐ
export { LoginForm } from './LoginForm';
export { useAuth } from './useAuth';
export type { User } from './types';
์ด๋ฌํ ์ ๊ทผ ๋ฐฉ์์ ๋ชจ๋์ ๊ณต๊ฐ API๋ฅผ ๋ช
ํํ ์ ์ํ๊ณ , ๋ถํ์ํ ์์กด์ฑ์ ๋ฐฉ์งํ๋ฉฐ, ์ฝ๋์ ๊ฐ๋
์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ํฅ์์ํต๋๋ค. ์ข ๋ ์์ธํ ์์๋ฅผ ๋ค์ด๋ณผ๊ฒ์.
์คํฌ๋ฆฝํธ์ ๋ํ ํด๋ ๊ตฌ์กฐ ์์:
project-root/
โโโ src/
โ โโโ features/
โ โ โโโ loginUser/
โ โ โโโ ui/
โ โ โโโ model/
โ โ โโโ index.ts (์๋ ์์ฑ๋ ํ์ผ)
โ โโโ ... (๋ค๋ฅธ FSD ํด๋๋ค)
โโโ scripts/
โ โโโ generate-barrel.ts
โโโ package.json
โโโ tsconfig.json
scripts/generate-barrel.ts ํ์ผ์ ๋ด์ฉ:
import fs from 'fs';
import path from 'path';
function generateBarrel(directory: string) {
const files = fs.readdirSync(directory);
const exports: string[] = [];
files.forEach(file => {
if (file.startsWith('_') || file === 'index.ts') return;
const filePath = path.join(directory, file);
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
exports.push(`export * from './${file}';`);
} else if (file.endsWith('.ts') || file.endsWith('.tsx')) {
const name = path.parse(file).name;
exports.push(`export { ${name} } from './${name}';`);
}
});
const content = exports.join('\n') + '\n';
fs.writeFileSync(path.join(directory, 'index.ts'), content);
}
const targetDir = process.argv[2];
if (!targetDir) {
console.error('Please specify a target directory');
process.exit(1);
}
const fullPath = path.resolve(process.cwd(), targetDir);
generateBarrel(fullPath);
console.log(`Barrel file generated for ${fullPath}`);
package.json์ ์คํฌ๋ฆฝํธ ์ถ๊ฐ:
{
"scripts": {
"api": "ts-node scripts/generate-barrel.ts"
}
}
์ด์ pnpm api features/loginUser ๋ช ๋ น์ด๋ฅผ ์คํํ๋ฉด, src/features/loginUser/index.ts ํ์ผ์ด ์๋์ผ๋ก ์์ฑ๋๊ฑฐ๋ ์ ๋ฐ์ดํธ๋ฉ๋๋ค! ์๋๋ถํฐ๋ ์ข ๋ ์ผ๋ฐ์ ์ธ ์์๋ฅผ ๋ค์ด๋ณผ๊ฒ์.
FSD ๊ธฐ๋ณธ ํด๋ ๊ตฌ์กฐ:
src/
โโโ app/ # ๊ธ๋ก๋ฒ ์ค์ , ์คํ์ผ, ํ๋ก๋ฐ์ด๋
โโโ processes/ # ๋น์ฆ๋์ค ํ๋ก์ธ์ค
โโโ pages/ # ํ์ด์ง ์ปดํฌ๋ํธ
โโโ widgets/ # ๋ณตํฉ UI ๋ธ๋ก
โโโ features/ # ์ฌ์ฉ์ ์ธํฐ๋์
โโโ entities/ # ๋น์ฆ๋์ค ์ํฐํฐ
โโโ shared/ # ๊ณต์ ์ ํธ๋ฆฌํฐ, UI ํคํธ
FSD์์์ ๋ฐฐ๋ด ํ์ผ ์ฌ์ฉ ์์
features/auth/index.ts (์ ์ ํ ๋ฐฐ๋ด ํ์ผ ์ฌ์ฉ):
// ๋ช
์์ ๋ด๋ณด๋ด๊ธฐ - ๊ณต๊ฐ API๋ฅผ ๋ช
ํํ ์ ์
export { LoginForm } from './ui/LoginForm';
export { useAuth } from './model/useAuth';
export type { AuthUser } from './model/types';
๊ท ํ ์กํ ์ ๊ทผ๋ฒ: ์ค์ ๊ตฌํ ์์
1. ๊ตฌ์กฐํ๋ ๋ฐฐ๋ด ํ์ผ ์ฌ์ฉ:
features/
โโโ auth/
โ โโโ ui/
โ โ โโโ LoginForm.tsx
โ โ โโโ RegisterForm.tsx
โ โโโ model/
โ โ โโโ useAuth.ts
โ โ โโโ types.ts
โ โโโ index.ts # ์ํคํ
์ฒ ๊ฒฝ๊ณ์๋ง ๋ฐฐ๋ด ํ์ผ ์ฌ์ฉ
โโโ userProfile/
โโโ ui/
โโโ model/
โโโ index.ts
2. ๋ช ์์ ๋ด๋ณด๋ด๊ธฐ:
// features/auth/index.ts
// ๋ช
์์ ๋ด๋ณด๋ด๊ธฐ๋ก ๊ณต๊ฐ API ์ ์
export { LoginForm } from './ui/LoginForm';
export { RegisterForm } from './ui/RegisterForm';
export { useAuth } from './model/useAuth';
export type { User, AuthCredentials } from './model/types';
3. FSD ์ํคํ ์ฒ ๊ท์น์ ๋ฐ๋ฅธ ๊ฐ์ ธ์ค๊ธฐ:
// ์๋ชป๋ ๋ฐฉ์: ๋ ์ด์ด ์ฐํ
import { Button } from '@/shared/ui/Button';
// ์ฌ๋ฐ๋ฅธ ๋ฐฉ์: ์ํคํ
์ฒ ๊ฒฝ๊ณ ์กด์ค
import { Button } from '@/shared/ui';
4. ์ค์ React ์ปดํฌ๋ํธ ์์:
// features/auth/ui/LoginForm.tsx
import { useState } from 'react';
import { Button, TextField } from '@/shared/ui';
import { useAuth } from '../model/useAuth';
import type { AuthCredentials } from '../model/types';
export const LoginForm = () => {
const [credentials, setCredentials] = useState({
email: '',
password: ''
});
const { login, isLoading } = useAuth();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
login(credentials);
};
return (
<TextField
label="Email"
value={credentials.email}
onChange={(e) => setCredentials(prev => ({
...prev,
email: e.target.value
}))}
/>
<TextField
type="password"
label="Password"
value={credentials.password}
onChange={(e) => setCredentials(prev => ({
...prev,
password: e.target.value
}))}
/>
{isLoading ? 'Logging in...' : 'Login'}
);
};
ํ๋์ ๋๊ตฌ์ ์ํฅ
์ต์ ๋น๋ ๋๊ตฌ๋ค์ ๋ฐฐ๋ด ํ์ผ์ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ์ผ๋ถ ์ํํ ์ ์์ต๋๋ค:
๋ฒ๋ค๋ฌ๋ณ ์ต์ ํ ์ค์ :
Vite ์ค์ ์์:
// vite.config.js
export default {
build: {
target: 'esnext',
minify: 'esbuild',
rollupOptions: {
output: {
manualChunks(id) {
// ์ต์ ํ๋ ์ฒญํฌ ์ค์
}
}
}
}
}
Webpack ์ค์ ์์:
// webpack.config.js
module.exports = {
optimization: {
usedExports: true, // ํธ๋ฆฌ ์์ดํน ํ์ฑํ
moduleIds: 'deterministic',
splitChunks: {
chunks: 'all',
}
}
}
์ฑ๋ฅ ์ธก์ - ์ค์๊ฐ ํ๋ก์ ํธ ๋ชจ๋ํฐ๋ง
// build-analyzer.ts
import { performance } from 'perf_hooks';
export function measureBuildTime(taskName: string, task: () => void) {
const startTime = performance.now();
task();
const endTime = performance.now();
console.log(`Task "${taskName}" took ${endTime - startTime}ms`);
}
// ์ฌ์ฉ ์์
measureBuildTime('Module compilation', () => {
// ๋น๋ ๋ก์ง
});
๊ฒฐ๋ก - ํ๋ก์ ํธ ๊ท๋ชจ๋ณ ๊ถ์ฅ ์ ๊ทผ๋ฒ
์๊ท๋ชจ ํ๋ก์ ํธ (10,000์ค ์ดํ)
src/
โโโ components/
โ โโโ index.ts # ๋ฐฐ๋ด ํ์ผ ์ฌ์ฉ ๊ฐ๋ฅ
โโโ hooks/
โ โโโ index.ts # ๋ฐฐ๋ด ํ์ผ ์ฌ์ฉ ๊ฐ๋ฅ
โโโ utils/
โโโ index.ts # ๋ฐฐ๋ด ํ์ผ ์ฌ์ฉ ๊ฐ๋ฅ
์ค๊ฐ ๊ท๋ชจ ํ๋ก์ ํธ (10,000~50,000์ค)
src/
โโโ features/
โ โโโ feature1/
โ โ โโโ index.ts # ์ํคํ
์ฒ ๊ฒฝ๊ณ์๋ง ๋ฐฐ๋ด ํ์ผ
โ โโโ feature2/
โ โโโ index.ts
โโโ shared/
โโโ ui/
โ โโโ index.ts # ๋ช
์์ ๋ด๋ณด๋ด๊ธฐ ์ฌ์ฉ
โโโ lib/
โโโ index.ts # ๋ช
์์ ๋ด๋ณด๋ด๊ธฐ ์ฌ์ฉ
๋๊ท๋ชจ ํ๋ก์ ํธ (50,000์ค ์ด์)
src/
โโโ app/
โโโ processes/
โโโ pages/
โ โโโ index.ts # ์๊ฒฉํ๊ฒ ์ ํ๋ ๋ฐฐ๋ด ํ์ผ
โโโ widgets/
โ โโโ index.ts # ์๊ฒฉํ๊ฒ ์ ํ๋ ๋ฐฐ๋ด ํ์ผ
โโโ features/
โ โโโ [๊ฐ ๊ธฐ๋ฅ๋ณ]/
โ โโโ index.ts # ๊ณต๊ฐ API๋ง ๋ช
์์ ๋ด๋ณด๋ด๊ธฐ
โโโ entities/
โ โโโ [๊ฐ ์ํฐํฐ๋ณ]/
โ โโโ index.ts # ๊ณต๊ฐ API๋ง ๋ช
์์ ๋ด๋ณด๋ด๊ธฐ
โโโ shared/
โโโ [๊ฐ ๋ชจ๋๋ณ]/
โโโ index.ts # ์ฑ๋ฅ ์ธก์ ํ ๊ฒฐ์
๋ฐฐ๋ด ํ์ผ์ ์ฌ์ฉ์ ๊ฒฐ๊ตญ ๊ฐ๋ฐ ๊ฒฝํ๊ณผ ์ฑ๋ฅ ์ฌ์ด์ ๊ท ํ์ ์ฐพ๋ ๋ฌธ์ ๋ผ ์๊ฐํฉ๋๋ค.
FSD ์ํคํ ์ฒ๋ฅผ ๋์ ํ๋ค๋ฉด, ๋ฐฐ๋ด ํ์ผ์ ์ฅ๋จ์ ์ ์ธ์ํ๊ณ ํ๋ก์ ํธ์ ๊ท๋ชจ์ ํ์ ํ์์ ๋ง๊ฒ ์ ์ ํ ์กฐ์ ํ๋ ๊ฒ์ด ์ค์ํ ๊ฒ ๊ฐ์์.
๋ช ํํ ์ํคํ ์ฒ ๊ฒฝ๊ณ, ๋ช ์์ ์ธ ๋ด๋ณด๋ด๊ธฐ, ๊ทธ๋ฆฌ๊ณ ํ๋์ ์ธ ๋น๋ ๋๊ตฌ๋ฅผ ํ์ฉํ๋ค๋ฉด, ๋ฐฐ๋ด ํ์ผ์ ์ด์ ์ ์ทจํ๋ฉด์๋ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ์ต์ํํ ์ ์์ต๋๋ค!