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

    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. ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ ์‚ฌ์ „ ๋ฐฉ์ง€
      • ํƒ€์ž… ์ฒดํฌ๋ฅผ ํ†ตํ•ด ๋Ÿฐํƒ€์ž„์— ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์˜ค๋ฅ˜๋ฅผ ๋ฏธ๋ฆฌ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค
      • ๋””๋ฒ„๊น… ์‹œ๊ฐ„์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค 

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

    ๋ฐ˜์‘ํ˜•

    ๋Œ“๊ธ€