๋ฆฌ์•กํŠธ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ -> ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํ•˜๊ธฐ!

1. ์ปดํฌ๋„ŒํŠธ ์„ ์–ธ ๋ฐฉ์‹

// ๊ธฐ์กด JS
const Dashboard = () => {
  // ...
}
// ๋ณ€ํ™˜๋œ TSX
const Dashboard: React.FC = () => {
  // ...
}
  • React.FC (Function Component) ํƒ€์ž…์„ ๋ช…์‹œ์ ์œผ๋กœ ์„ ์–ธํ•˜์—ฌ TypeScript์—๊ฒŒ ์ด ์ปดํฌ๋„ŒํŠธ๊ฐ€ React ์ปดํฌ๋„ŒํŠธ์ž„์„ ์•Œ๋ ค์ค€๋‹ค
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ์˜ props ํƒ€์ž…์„ ์ž๋™์œผ๋กœ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, React ๊ด€๋ จ ํƒ€์ž… ์ฒดํฌ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค
  • ์ตœ์‹  React์—์„œ๋Š” React.FC ๋Œ€์‹  React.ComponentType์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๋‹ค

 

2. Props ํƒ€์ž… ์ •์˜

// ๊ธฐ์กด JS
const CategoryEditor = ({ onClose }) => {
  // ...
}
// ๋ณ€ํ™˜๋œ TSX
interface CategoryEditorProps {
  onClose: (value: boolean) => void;
}

const CategoryEditor: React.FC<CategoryEditorProps> = ({ onClose }) => {
  // ...
}
  • interface๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ์˜ props ํƒ€์ž…์„ ๋ช…์‹œ์ ์œผ๋กœ ์ •์˜ํ•œ๋‹ค
  • onClose ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ ์‹œ ํ•„์š”ํ•œ props๋ฅผ ๋ˆ„๋ฝํ•˜๊ฑฐ๋‚˜ ์ž˜๋ชป๋œ ํƒ€์ž…์„ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค 

 

3. ์ƒํƒœ(State) ํƒ€์ž… ์ •์˜

// ๊ธฐ์กด JS
const [course, setCourse] = useState(null);
// ๋ณ€ํ™˜๋œ TSX
const [course, setCourse] = useState<Course | null>(null);
  • useState ํ›…์— ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ์˜ ํƒ€์ž…์„ ๋ช…์‹œํ•œ๋‹ค
  • Course | null์€ course๊ฐ€ Course ํƒ€์ž…์ด๊ฑฐ๋‚˜ null์ผ ์ˆ˜ ์žˆ์Œ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํƒ€์ž… ์•ˆ์ „์„ฑ์ด ๋ณด์žฅ๋˜๋ฉฐ, IDE์˜ ์ž๋™์™„์„ฑ ๊ธฐ๋Šฅ๋„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค 

 

4. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ํƒ€์ž…

// ๊ธฐ์กด JS
const handleSubmit = (e) => {
  e.preventDefault();
  // ...
}
// ๋ณ€ํ™˜๋œ TSX
const handleSubmit = (e: React.FormEvent) => {
  e.preventDefault();
  // ...
}
  • ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์— React.FormEvent ํƒ€์ž…์„ ์ง€์ •ํ•œ๋‹ค
  • ์ด๋Š” ํผ ์ œ์ถœ ์ด๋ฒคํŠธ์˜ ํƒ€์ž…์„ ๋ช…ํ™•ํžˆ ํ•˜์—ฌ, ์ด๋ฒคํŠธ ๊ฐ์ฒด์˜ ์†์„ฑ์— ์•ˆ์ „ํ•˜๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค
  • ๋‹ค๋ฅธ ์ด๋ฒคํŠธ ํƒ€์ž…์œผ๋กœ๋Š” React.MouseEvent, React.ChangeEvent ๋“ฑ์ด ์žˆ๋‹ค

 

5. styled-components ํƒ€์ž…

// ๊ธฐ์กด JS
const Chip = styled.button`
  background-color: ${props => props.active ? "#007bff" : "white"};
`
// ๋ณ€ํ™˜๋œ TSX
const Chip = styled.button<{ active: boolean }>`
  background-color: ${props => props.active ? "#007bff" : "white"};
`
  • styled-components์—์„œ props์˜ ํƒ€์ž…์„ ์ œ๋„ค๋ฆญ์œผ๋กœ ์ •์˜ํ•œ๋‹ค
  • active prop์ด boolean ํƒ€์ž…์ž„์„ ๋ช…์‹œํ•˜์—ฌ, ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ ์‹œ ํƒ€์ž… ์ฒดํฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์Šคํƒ€์ผ ์ปดํฌ๋„ŒํŠธ์˜ props์— ๋Œ€ํ•œ ํƒ€์ž… ์•ˆ์ „์„ฑ์ด ๋ณด์žฅ๋œ๋‹ค 

 

6. Redux ๊ด€๋ จ ํƒ€์ž… ์ •์˜

// ๊ธฐ์กด JS
const dispatch = useDispatch();
const { categories } = useSelector(state => state.category);
// ๋ณ€ํ™˜๋œ TSX
const dispatch = useAppDispatch();
const { categories } = useAppSelector((state: RootState) => state.category);
  • Redux์˜ dispatch์™€ useSelector์— ํƒ€์ž…์„ ์ถ”๊ฐ€ํ•˜์—ฌ ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค
  • RootState ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜์—ฌ ์ „์ฒด Redux ์ƒํƒœ์˜ ๊ตฌ์กฐ๋ฅผ ๋ช…ํ™•ํžˆ ํ•œ๋‹ค
  • ์ปค์Šคํ…€ ํ›…์ธ useAppDispatch์™€ useAppSelector๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํƒ€์ž…์ด ์ง€์ •๋œ dispatch์™€ selector๋ฅผ ์ œ๊ณตํ•œ๋‹ค 

 

7. Firebase ๋ฐ์ดํ„ฐ ํƒ€์ž… ์ •์˜

// ๊ธฐ์กด JS
const coursesData = coursesSnapshot.docs.map(doc => ({
  id: doc.id,
  ...doc.data()
}));
// ๋ณ€ํ™˜๋œ TSX
const coursesData = coursesSnapshot.docs.map(doc => ({
  id: doc.id,
  ...doc.data()
})) as Course[];
  • Firebase์—์„œ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ์— Course ํƒ€์ž…์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•œ๋‹ค
  • as Course[]๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํƒ€์ž… ๋‹จ์–ธ(type assertion)์„ ์ˆ˜ํ–‰ํ•œ๋‹ค
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด Firebase ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํƒ€์ž… ์•ˆ์ „์„ฑ์ด ๋ณด์žฅ๋œ๋‹ค 

 

8. ํƒ€์ž… ๊ฐ€๋“œ ์ถ”๊ฐ€

// ๊ธฐ์กด JS
filtered = selectedCat.courseDetails.filter(course => course != null);
// ๋ณ€ํ™˜๋œ TSX
filtered = (selectedCat.courseDetails || []).filter((course): course is Course => course !== null);
  • ํƒ€์ž… ๊ฐ€๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ null์ด ์•„๋‹Œ Course ๊ฐ์ฒด๋งŒ ํ•„ํ„ฐ๋งํ•œ๋‹ค
  • course is Course๋Š” ํƒ€์ž… ๊ฐ€๋“œ๋กœ, ํ•„ํ„ฐ๋ง๋œ ๋ฐฐ์—ด์ด Course[] ํƒ€์ž…์ž„์„ ๋ณด์žฅํ•œ๋‹ค
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด null ๊ฐ’์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค 

 

9. ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜ ์ถ”๊ฐ€

export interface Course {
  id: string;
  courseName: string;
  courseLength: number;
  description: string;
  locationInfo?: {
    latitude: number;
    longitude: number;
  };
}

export interface Category {
  id: string;
  title: string;
  courseIdList: string[];
  courseDetails?: (Course | null)[];
}
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์ฃผ์š” ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ •์˜ํ•œ๋‹ค
  • ?๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ ํƒ์  ์†์„ฑ์„ ํ‘œ์‹œํ•œ๋‹ค
  • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ exportํ•˜์—ฌ ๋‹ค๋ฅธ ํŒŒ์ผ์—์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค 

 

10. ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํƒ€์ž… ์ •์˜

// ๊ธฐ์กด JS
process.env.REACT_APP_FIREBASE_API_KEY
// ๋ณ€ํ™˜๋œ TSX
declare global {
  namespace NodeJS {
    interface ProcessEnv {
      REACT_APP_FIREBASE_API_KEY: string;
      // ... ๋‹ค๋ฅธ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋“ค
    }
  }
}
  • ProcessEnv ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™•์žฅํ•˜์—ฌ ํ™˜๊ฒฝ ๋ณ€์ˆ˜์˜ ํƒ€์ž…์„ ์ •์˜ํ•œ๋‹ค
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํƒ€์ž… ์ฒดํฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค
  • ํ•„์ˆ˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ๋ˆ„๋ฝ๋˜์—ˆ์„ ๋•Œ ์ปดํŒŒ์ผ ์‹œ์ ์— ์˜ค๋ฅ˜๋ฅผ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ๋‹ค 

 

์ฃผ์š” ๊ฐœ์„  ํšจ๊ณผ

  1. ํƒ€์ž… ์•ˆ์ „์„ฑ ํ–ฅ์ƒ
    • ์ปดํŒŒ์ผ ์‹œ์ ์— ํƒ€์ž… ๊ด€๋ จ ์˜ค๋ฅ˜๋ฅผ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ๋‹ค
    • ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค 
  2. ์ฝ”๋“œ ์ž๋™์™„์„ฑ ์ง€์›
    • IDE์—์„œ ๋” ์ •ํ™•ํ•œ ์ž๋™์™„์„ฑ์„ ์ œ๊ณตํ•œ๋‹ค
    • ์ฝ”๋“œ ์ž‘์„ฑ ์†๋„๊ฐ€ ํ–ฅ์ƒ๋œ๋‹ค 
  3. ์‹ค์ˆ˜ ๋ฐฉ์ง€
    • ํ•„์ˆ˜ props ๋ˆ„๋ฝ์„ ๋ฐฉ์ง€ํ•œ๋‹ค
    • ์ž˜๋ชป๋œ ํƒ€์ž…์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•œ๋‹ค 
  4. ์ฝ”๋“œ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ
    • ํƒ€์ž… ์ •์˜๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์˜๋„๋ฅผ ๋ช…ํ™•ํžˆ ํ•  ์ˆ˜ ์žˆ๋‹ค
    • ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์›Œ์ง„๋‹ค 
  5. ๋ฆฌํŒฉํ† ๋ง ์‹œ ์•ˆ์ •์„ฑ ํ–ฅ์ƒ
    • ํƒ€์ž… ์‹œ์Šคํ…œ์„ ํ†ตํ•ด ๋ฆฌํŒฉํ† ๋ง ์‹œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฅ˜๋ฅผ ๋ฏธ๋ฆฌ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ๋‹ค
    • ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ ์˜ํ–ฅ ๋ฒ”์œ„๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ ์‰ฌ์›Œ์ง„๋‹ค 
  6. ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ ์‚ฌ์ „ ๋ฐฉ์ง€
    • ํƒ€์ž… ์ฒดํฌ๋ฅผ ํ†ตํ•ด ๋Ÿฐํƒ€์ž„์— ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฅ˜๋ฅผ ๋ฏธ๋ฆฌ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค
    • ๋””๋ฒ„๊น… ์‹œ๊ฐ„์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค 

์ด๋Ÿฌํ•œ ๋ณ€๊ฒฝ์‚ฌํ•ญ๋“ค๋กœ ์ธํ•ด ์ฝ”๋“œ์˜ ํ’ˆ์งˆ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋œ๋‹ค! 

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€