๐Ÿ“ฆ 04. ์ œ๋„ค๋ฆญ(Generics): ์˜์ˆ˜๋„ค ๋งŒ๋Šฅ API ์‘๋‹ต ๋ž˜ํผ ๋งŒ๋“ค๊ธฐ

2026๋…„ 3์›” 5์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

ํƒ€์ž…์„ ๋ณ€์ˆ˜์ฒ˜๋Ÿผ ๋„˜๊ฒจ์ฃผ๋Š” ๋งˆ๋ฒ•, ๊ทธ๋ฆฌ๊ณ  extends๋ฅผ ํ™œ์šฉํ•œ ์ œ๋„ค๋ฆญ ์ œ์•ฝ ์กฐ๊ฑด

๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 12๋ถ„(์ „์ฒด) / ํ•ต์‹ฌ ํŒŒํŠธ๋งŒ: 7๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
[๋ฐ˜๋ณต๋˜๋Š” ํƒ€์ž… ๋ณต๋ถ™์˜ ๋Šช] โ†’ [์ œ๋„ค๋ฆญ: ํƒ€์ž…์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ํ™”] โ†’ [์•„๋ฌด๊ฑฐ๋‚˜ ๋“ค์–ด์˜ค๋Š” ๊ฑด ์‹ซ์–ด: extends ์ œ์•ฝ]

๐ŸŽฏ ์ด ๋ฌธ์„œ๋ฅผ ๋‹ค ์ฝ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • <T> ๊ฐ€ ํ•จ์ˆ˜ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์ •ํ™•ํžˆ ๋˜‘๊ฐ™์€ ๊ฐœ๋…์ž„์„ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฐ˜๋ณต๋˜๋Š” API ์‘๋‹ต ๊ตฌ์กฐ๋ฅผ ํ•˜๋‚˜์˜ ์ œ๋„ค๋ฆญ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์šฐ์•„ํ•˜๊ฒŒ ํ†ต์ผํ•  ์ˆ˜ ์žˆ๋‹ค.
  • extends ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ์ œ๋„ค๋ฆญ์ด ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…์˜ ๋ฒ”์œ„๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ œํ•œํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ๐Ÿฃ ์˜์ฒ  ( ์‹ ์ž… ): "(ํƒ€๋‹ฅํƒ€๋‹ฅ... ์•— ๋ณต์‚ฌ ๋ถ™์—ฌ๋„ฃ๊ธฐ ์‚‘์‚ด๋‚ฌ๋‹ค) ํ•˜์•„... API ์—”๋“œํฌ์ธํŠธ๊ฐ€ ๋Š˜์–ด๋‚  ๋•Œ๋งˆ๋‹ค ApiResponse_User, ApiResponse_Board, ApiResponse_Comment ๋‹ค ๋”ฐ๋กœ ๋งŒ๋“ค๊ณ  ์žˆ๋Š”๋ฐ, statusCode๋ž‘ message ํ•„๋“œ๋Š” ๋งจ๋‚  ๋˜‘๊ฐ™์ด ๋“ค์–ด๊ฐ€๋„ค์š”. ์†๊ฐ€๋ฝ ์•„ํŒŒ ์ฃฝ๊ฒ ์Šต๋‹ˆ๋‹ค."
  • ๐Ÿฆ ์˜ํ˜ธ ( ๋ฆฌ๋“œ ): "์˜์ฒ  ๋‹˜, ํ˜น์‹œ ํ•จ์ˆ˜ ๋งŒ๋“ค ๋•Œ ์ค‘๋ณต๋˜๋Š” ๊ฐ’์ด ์žˆ์œผ๋ฉด ๋งค๋ฒˆ ํ•จ์ˆ˜๋ฅผ 100๊ฐœ ๋งŒ๋“œ๋‚˜์š”? ๋งค๊ฐœ๋ณ€์ˆ˜(Parameter)๋กœ ๋นผ๋ฒ„๋ฆฌ์ž–์•„์š”. ํƒ€์ž…๋„ ๋˜‘๊ฐ™์•„์š”. ๋ณ€ํ•˜๋Š” ๋ถ€๋ถ„๋งŒ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ฒจ๋ฐ›๋Š” ๋งˆ๋ฒ•, ๊ทธ๊ฒŒ ๋ฐ”๋กœ ์ œ๋„ค๋ฆญ(Generics)์ž…๋‹ˆ๋‹ค."

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€: '๋ณต๋ถ™'์€ ์ฃ„์•…์ด๋‹ค

ํ˜„๋Œ€์ ์ธ ํ”„๋ก ํŠธ์—”๋“œ ํ™˜๊ฒฝ์—์„œ ์™ธ๋ถ€์™€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›์„ ๋•Œ, ๋ฐฑ์—”๋“œ ๊ตฌ์„ฑ์›๋“ค์€ ๋Œ€์ฒด๋กœ ๊ณตํ†ต๋œ ๋ด‰ํˆฌ(Wrapper) ์•ˆ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์•„์„œ ์ค๋‹ˆ๋‹ค.

์ด๋ฅผํ…Œ๋ฉด, ์˜์ˆ˜๋„ค ๋ฐฑ์—”๋“œ๋Š” ํ•ญ์ƒ ์ด๋Ÿฐ ํฌ๋งท์„ ๊ณ ์ง‘ํ•ฉ๋‹ˆ๋‹ค.

{
  "statusCode": 200,
  "message": "์„ฑ๊ณต!",
  "data": { ...์—ฌ๊ธฐ์— ์ง„์งœ ๋ฐ์ดํ„ฐ... }
}

๋งŒ์•ฝ ์ œ๋„ค๋ฆญ์„ ๋ชจ๋ฅธ๋‹ค๋ฉด, ์˜์ฒ ์ด์˜ ์ฝ”๋“œ๋Š” ๋”์ฐํ•˜๊ฒŒ ๋น„๋Œ€ํ•ด์งˆ ์ˆ˜๋ฐ–์— ์—†์Šต๋‹ˆ๋‹ค.

๐Ÿ’ฃ ์˜์ฒ ์ด์˜ ๊ณผ๊ฑฐ (๋…ธ๊ฐ€๋‹ค ํƒ€์ž… ์„ ์–ธ)

// ์œ ์ €๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ
interface UserResponse {
  statusCode: number;
  message: string;
  data: { id: number; name: string };
}
 
// ๊ฒŒ์‹œ๊ธ€์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ
interface BoardResponse {
  statusCode: number; // ์ค‘๋ณต!
  message: string;    // ์ค‘๋ณต!
  data: { id: number; title: string; content: string }[];
}

์ด ์ฝ”๋“œ๋Š” ์‹ค๋ฌด์—์„œ 100๊ฐœ์˜ API๋ฅผ ๋ถ™์ด๋ฉด 100๋ฒˆ ๋˜‘๊ฐ™์€ statusCode์™€ message๋ฅผ ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ค ๋ฐฑ์—”๋“œ๊ฐ€ message๋ฅผ msg๋กœ ๋ฐ”๊พธ๋ฉด? ์•ผ๊ทผ ํ™•์ •์ž…๋‹ˆ๋‹ค.


๐ŸŽฉ 1. ์ œ๋„ค๋ฆญ์˜ ๋ณธ์งˆ: ํƒ€์ž…๋„ ๋ณ€์ˆ˜๋‹ค

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํ•จ์ˆ˜์˜ ํŒจ๋Ÿฌ๋‹ค์ž„์„ ํƒ€์ž…์˜ ์„ธ๊ณ„๋กœ ํ›”์ณ ์˜ต๋‹ˆ๋‹ค.

function hello(name) ์ด๋ผ๋Š” ํ•จ์ˆ˜๊ฐ€ ์žˆ์„ ๋•Œ, ๊ด„ํ˜ธ ์•ˆ์˜ name ์ž๋ฆฌ์— "์˜์ฒ "์„ ๋„ฃ์„์ง€ "์˜ํ˜ธ"๋ฅผ ๋„ฃ์„์ง€๋Š” ๋‚˜์ค‘์— ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.
์ œ๋„ค๋ฆญ๋„ ์™„์ „ํžˆ ๋˜‘๊ฐ™์Šต๋‹ˆ๋‹ค. ๊บพ์‡ (< >) ์•ˆ์— ์“ธ ์•ŒํŒŒ๋ฒณ(์ฃผ๋กœ T, U, A ๋“ฑ์„ ์”๋‹ˆ๋‹ค)์€ ๋‚˜์ค‘์— ๊ฒฐ์ •๋  ๋ฐ์ดํ„ฐ์˜ ๋ชจ์–‘์„ ๋‹ด์•„๋‘˜ "๋ณ€์ˆ˜ ๋ฐ”๊ตฌ๋‹ˆ"์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

๐Ÿฆ ์˜ํ˜ธ์˜ ์šฐ์•„ํ•œ ๋งŒ๋Šฅ ๋ž˜ํผ (Generic Wrapper)

// ์˜ํ˜ธ: "์ž, ๋ฐ”๋€Œ์ง€ ์•Š๋Š” ๋ด‰ํˆฌ(statusCode, message)๋Š” ๋†”๋‘๊ณ ,
// ์•ˆ์— ๋ฐ”๋€” ์•Œ๋งน์ด(data) ์ž๋ฆฌ๋งŒ <T> ๋ผ๋Š” ๋ณ€์ˆ˜๋กœ ๋šซ์–ด๋‘๋Š” ๊ฒ๋‹ˆ๋‹ค."
 
interface ApiResponse<T> {
  statusCode: number;
  message: string;
  data: T; // ์—ฌ๊ธฐ์— ๋‚˜์ค‘์— ๋„˜๊ฒจ์ค€ ํƒ€์ž…์ด ๊ทธ๋Œ€๋กœ ๋ฐ•ํž˜!
}

์ด์ œ ์˜์ฒ ์ด๋Š” API ์‘๋‹ต์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•œ ๋ฐฉ์— ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

type User = { id: number; name: string };
type Board = { id: number; title: string; content: string };
 
// ์‚ฌ์šฉํ•  ๋•Œ, < > ์•ˆ์— ์›ํ•˜๋Š” ์•Œ๋งน์ด๋ฅผ ๋˜์ ธ์ค€๋‹ค!
const userRes: ApiResponse<User> = await fetchUser();
const boardsRes: ApiResponse<Board[]> = await fetchBoards(); // ๋ฐฐ์—ด๋„ ํ†ต์งธ๋กœ ๋„ฃ์„ ์ˆ˜ ์žˆ์Œ!

๐Ÿ’ก ์™œ ํ•˜ํ•„ ๋Œ€๋ฌธ์ž T์ธ๊ฐ€์š”?
Type์˜ ์•ฝ์ž์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜ ๋ณ€์ˆ˜๋ฅผ a, b, c๋กœ ์ง“๋“ฏ ์ œ๋„ค๋ฆญ์—์„œ๋Š” T, U, V๋ฅผ ๊ด€์Šต์ ์œผ๋กœ ์”๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ถŒ์žฅํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. 5๋…„ ์ฐจ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๊ทธ๋ƒฅ T ๋ง๊ณ  ์˜๋ฏธ ์žˆ๋Š” ์ด๋ฆ„(<TData>, <TResponse>)์„ ์“ฐ๋Š” ๊ฒƒ์ด ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์— ํ›จ์”ฌ ์ข‹์Šต๋‹ˆ๋‹ค.


๐Ÿšซ 2. ์ œ๋„ค๋ฆญ ์ œ์•ฝ (Generic Constraints): ์•„๋ฌด๊ฑฐ๋‚˜ ๋„ฃ์ง€ ๋งˆ!

ํ•˜์ง€๋งŒ ์ œ๋„ค๋ฆญ์ด ๋ฌด์กฐ๊ฑด ๋šซ๋ ค์žˆ๋Š” ๋นˆ ์ข…์ด์—ฌ์„œ๋Š” ์•ˆ ๋  ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜์ฒ ์ด๊ฐ€ ์œ ์ €์˜ ๊ธธ์ด๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ์œ ํ‹ธ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค๊ณ  ํ•ฉ์‹œ๋‹ค.

// ๐Ÿฃ ์˜์ฒ : "T๋Š” ๋งŒ๋Šฅ์ด๋‹ˆ๊นŒ ๋ฐฐ์—ด์ด๋“  ๋ฌธ์ž์—ด์ด๋“  ๋‹ค ๋ฐ›์•„๋‚ด๊ฒ ์ง€!"
function logLength<T>(items: T): void {
  // ๐Ÿ’ฃ ์—๋Ÿฌ! T๊ฐ€ ๋ญ”์ง€ ๋ชจ๋ฅด๋Š”๋ฐ length ์†์„ฑ์ด ์žˆ์„ ์ค„ ์–ด๋–ป๊ฒŒ ์•Œ์•„?
  console.log(items.length); 
}

์ด๋•Œ ์˜ํ˜ธ ๋ฆฌ๋“œ๊ฐ€ ๋“ฑํŒํ•ฉ๋‹ˆ๋‹ค. "์˜์ฒ  ๋‹˜, TSํ•œํ…Œ ์ตœ์†Œํ•œ ์ด T๊ฐ€ length๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋Š” ๊ฑด ์•Œ๋ ค์ค˜์•ผ์ง€ ์•„๋ฌด๊ฑฐ๋‚˜ ๋‹ค ๋ฐ›์•„์ฃผ๋ฉด ์–ด๋–กํ•ฉ๋‹ˆ๊นŒ?"

extends ๋กœ ํƒ€์ž…์˜ ์„ฑ์งˆ ์ œํ•œํ•˜๊ธฐ

์—ฌ๊ธฐ์„œ์˜ extends๋Š” ํด๋ž˜์Šค์˜ ์ƒ์†์ด๋ผ๊ธฐ๋ณด๋‹ค "์ตœ์†Œํ•œ ์ด ์กฐ๊ฑด์€ ๋งŒ์กฑํ•ด์•ผ ํ•ด!" ๋ผ๋Š” ๊ฒ€๋ฌธ์†Œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

// "T๋ผ๋Š” ๋ณ€์ˆ˜๋ฅผ ๋ฐ›์„ ๊ฑด๋ฐ, ๊ฑ”๋Š” ๋ฌด์กฐ๊ฑด length ์†์„ฑ(number)์„ ๊ฐ€์ง„ ๋…€์„์ด์–ด์•ผ ํ•ด!"
interface HasLength {
  length: number;
}
 
function safeLogLength<T extends HasLength>(items: T): void {
  // ์ด์ œ TS๋Š” T๊ฐ€ ๋ฌด์กฐ๊ฑด length๋ฅผ ๊ฐ€์กŒ๋‹ค๊ณ  ํ™•์‹ ํ•จ. ์—๋Ÿฌ ์‚ฌ๋ผ์ง!
  console.log(items.length); 
}
 
// ์‚ฌ์šฉ ์˜ˆ์‹œ
safeLogLength([1, 2, 3]); // โœ… ๋ฐฐ์—ด์€ length๊ฐ€ ์žˆ์Œ
safeLogLength("์•ˆ๋…•ํ•˜์„ธ์š”"); // โœ… ๋ฌธ์ž์—ด๋„ length๊ฐ€ ์žˆ์Œ
safeLogLength({ length: 10, name: "๋ฃฐ๋ฃจ" }); // โœ… ๊ฐ์ฒด ์•ˆ์— length ์†์„ฑ์„ ๋„ฃ์—ˆ์œผ๋‹ˆ ํ†ต๊ณผ!
 
// safeLogLength(123); // โŒ ์‚๋น…! ์ˆซ์ž๋Š” length๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค!

์ด์ฒ˜๋Ÿผ extends๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์ œ๋„ค๋ฆญ์˜ ๋ฌดํ•œํ•œ ์ž์œ ๋„๋ฅผ ์•ˆ์ „ํ•œ ํ…Œ๋‘๋ฆฌ ์•ˆ์œผ๋กœ ์ขํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

Q1. interface ApiResponse<T> ์—์„œ ๊บพ์‡ (<T>) ์•ˆ์— ๋“ค์–ด๊ฐ€๋Š” T์˜ ์˜๋ฏธ๋ฅผ ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋น„๊ตํ•˜์—ฌ ์„ค๋ช…ํ•ด ๋ณด์„ธ์š”.

โœ… ์ •๋‹ต: T๋Š” ํ•จ์ˆ˜ ์„ ์–ธ๋ถ€์˜ ๋งค๊ฐœ๋ณ€์ˆ˜(Parameter)์™€ ์™„๋ฒฝํžˆ ๋˜‘๊ฐ™์€ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • function print(text)์—์„œ text๊ฐ€ ๋Ÿฐํƒ€์ž„์˜ ์‹ค์ œ '๊ฐ’'์„ ๋‚˜์ค‘์— ๋ฐ›์•„์˜ค๊ธฐ ์œ„ํ•œ ๋นˆ๋ฒˆ์ˆ˜ ์ด๋“ฏ์ด,
  • <T>๋Š” ์ปดํŒŒ์ผ ํƒ€์ž„์— ์‚ฌ์šฉํ•  ์‹ค์ œ 'ํƒ€์ž…'์˜ ๊ป๋ฐ๊ธฐ๋ฅผ ๋‚˜์ค‘์— ๋ฐ›์•„์˜ค๊ธฐ ์œ„ํ•ด ๋ฏธ๋ฆฌ ์ž๋ฆฌ๋ฅผ ๋น„์›Œ๋‘๋Š” ํƒ€์ž… ๋ ˆ๋ฒจ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์ผ ๋ฟ์ž…๋‹ˆ๋‹ค.

Q2. ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜๋ฅผ ์„ ์–ธํ•  ๋•Œ, ์ธ์ž๋กœ ๋„˜์–ด์˜ค๋Š” ์ œ๋„ค๋ฆญ T๊ฐ€ ๋ฐ˜๋“œ์‹œ ํŠน์ • ํ”„๋กœํผํ‹ฐ(์˜ˆ: id)๋ฅผ ํฌํ•จํ•˜๋„๋ก ๊ฐ•์ œํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํ‚ค์›Œ๋“œ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?

โœ… ์ •๋‹ต: extends ํ‚ค์›Œ๋“œ์ž…๋‹ˆ๋‹ค. (์˜ˆ: <T extends { id: number }>)

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์ œ๋„ค๋ฆญ ์ œ์•ฝ ์กฐ๊ฑด(Constraints)์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.
  • ์•„๋ฌด ์ œ์•ฝ์ด ์—†๋Š” T๋Š” TS ์ž…์žฅ์—์„œ unknown๊ณผ ๋‹ค๋ฅผ ๋ฐ” ์—†์–ด ๋‚ด๋ถ€ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋งˆ์Œ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. extends๋ฅผ ํ†ตํ•ด ์ตœ์†Œํ•œ์˜ ๊ตฌ์กฐ์  ์š”๊ตฌ์‚ฌํ•ญ์„ TS์—๊ฒŒ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Q3. [์˜์ฒ ์ด์˜ ํ…Œ์ŠคํŠธ ํƒ€์ž„: ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„] ํŒ€ ํšŒ์˜ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. ์˜์ˆ˜(PM/๋ฐฑ์—”๋“œ)๊ฐ€ "์š”์ฆ˜ API ํด๋ผ์ด์–ธํŠธ๋กœ Axios ๋ง๊ณ  ๋‚ด์žฅ fetch๋ฅผ ์“ฐ์ž"๊ณ  ์ œ์•ˆํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ์ž ์˜์ฒ ์ด๊ฐ€ ๋ฐ˜๋ฐœํ•ฉ๋‹ˆ๋‹ค. "Axios๋Š” axios.get<User>('/api') ์ฒ˜๋Ÿผ ์ œ๋„ค๋ฆญ์„ ๋ฐ”๋กœ ์ง€์›ํ•ด์„œ ํŽธํ•œ๋ฐ, ์ˆœ์ˆ˜ fetch๋Š” ์ œ๊ฐ€ ๋งค๋ฒˆ (await res.json()) as User๋กœ ์บ์ŠคํŒ…ํ•ด์ค˜์•ผ ํ•˜์ž–์•„์š”! ๋„ˆ๋ฌด ๋ถˆํŽธํ•ฉ๋‹ˆ๋‹ค." ์˜ํ˜ธ ๋ฆฌ๋“œ๋Š” ์ด ์ƒํ™ฉ์„ 5๋ถ„ ๋งŒ์— ์–ด๋–ค ์ปค์Šคํ…€ ์œ ํ‹ธ ํ•จ์ˆ˜๋กœ ํ•ด๊ฒฐํ•ด ์ค„ ์ˆ˜ ์žˆ์„๊นŒ์š”?

โœ… ์ •๋‹ต: fetch๋ฅผ ๊ฐ์‹ธ๊ณ  ์ œ๋„ค๋ฆญ <T>๋ฅผ ๋ฐ›์•„ ์‘๋‹ต๊ฐ’์„ Promise<T>๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ž˜ํผ(Wrapper) ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

๐Ÿ’ก ์ƒ์„ธ ํ•ด์„ค:

  • ์›๋ฆฌ ์„ค๋ช…: ์ œ๋„ค๋ฆญ์€ ๋‚ด์žฅ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋ถˆํŽธํ•จ์„ ์šฐ์•„ํ•˜๊ฒŒ ๋ฎ๋Š” ํ›Œ๋ฅญํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์€ ์ปค์Šคํ…€ fetch ๋ž˜ํผ๋ฅผ ๋‹จ ํ•œ ๋ฒˆ๋งŒ ์„ ์–ธํ•ด๋‘๋ฉด, ์˜จ ํ”„๋กœ์ ํŠธ์—์„œ Axios ๋ถ€๋Ÿฝ์ง€ ์•Š๊ฒŒ ํƒ€์ž…์„ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    async function myFetch<T>(url: string): Promise<T> {
      const response = await fetch(url);
      if (!response.ok) throw new Error('API ๐Ÿ’ฃ');
      return response.json(); // ์•”๋ฌต์ ์œผ๋กœ any๊ฐ€ ๋ฐ˜ํ™˜๋˜์ง€๋งŒ, ๋ฐ˜ํ™˜ ํƒ€์ž…์ด Promise<T>์ด๋ฏ€๋กœ ์•Œ์•„์„œ ์บ์ŠคํŒ…๋จ
    }
     
    // ์˜์ฒ ์ด์˜ ์‚ฌ์šฉ์ฒ˜: 
    // const user = await myFetch<User>('/api/user');
  • ์˜ค๋‹ต ํ”ผ๋“œ๋ฐฑ: "์˜์ฒ  ๋‹˜, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ํŽธํ•˜๋‹ค๊ณ  ์˜์กด์„ฑ์— ๋ชฉ๋งค๋‹ฌ์ง€ ๋งˆ์„ธ์š”. TypeScript์˜ ์ œ๋„ค๋ฆญ ์›๋ฆฌ๋งŒ ์•Œ๋ฉด ์šฐ๋ฆฌ๊ฐ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ฒ˜๋Ÿผ ๋งŒ๋“ค์–ด์„œ ์“ฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์•„, ๋ฌผ๋ก  ์ €๊ธฐ์„œ json()์ด any๋ฅผ ๋ฑ‰๋Š” ๊ฑด ๋Ÿฐํƒ€์ž„ ๋ถˆ์•ˆ์š”์†Œ์ง€๋งŒ, Axios๋„ ๊ฒฐ๊ตญ ๋˜‘๊ฐ™์€ ์ง“์„ ํ•˜๊ณ  ์žˆ์œผ๋‹ˆ ์ผ๋‹จ ๋„˜์–ด๊ฐ‘์‹œ๋‹ค."
  • ๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: ๊ป๋ฐ๊ธฐ๋Š” ๋‚ด๊ฐ€ ๋งŒ๋“ค ํ…Œ๋‹ˆ, ์ตœ์ข… ๊ฒฐ๊ณผ๋ฌผ(ํƒ€์ž…)์€ ํ˜ธ์ถœํ•˜๋Š” ์ชฝ(๋„ˆ)์ด ๋˜์ ธ์ค˜!

๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

๐Ÿ’ก "์ œ๋„ค๋ฆญ์€ ์ฝ”๋“œ๊ณ„์˜ ๋ถ•์–ด๋นต ๋นตํ‹€์ด๋‹ค. ๋ฐ˜์ฃฝ์ด ํŒฅ์ด๋“  ์Šˆํฌ๋ฆผ์ด๋“ , ๋ด‰ํˆฌ(Wrapper)๋Š” ์šฐ์•„ํ•˜๊ฒŒ ํ˜•ํƒœ๋ฅผ ์œ ์ง€ํ•ด ๋‚ธ๋‹ค."

๋‚ด๊ฐ€ ๋ฉฐ์น  ๋™์•ˆ interface ๋ณต๋ถ™ ๋…ธ๊ฐ€๋‹ค๋ฅผ ๋›ฐ๋ฉฐ ์‚ฝ์งˆํ•œ ๊ฑธ, ์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์€ ๋‹จ ๋‘ ์ค„์˜ ์ฝ”๋“œ๋กœ ์šฐ์•„ํ•˜๊ฒŒ ๋๋‚ด๋ฒ„๋ ธ๋‹ค. <T> ํ•œ ๊ธ€์ž์˜ ์œ„๋ ฅ์ด ์ด๋ ‡๊ฒŒ ๋ฌด์„œ์šธ ์ค„์ด์•ผ.
๊ทธ๋™์•ˆ ๋‚จ๋“ค์ด ๋งŒ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ (React์˜ useState<number>()๋‚˜ axios<Res>()) ์“ธ ๋• ์•„๋ฌด ์ƒ๊ฐ ์—†์ด ๊บพ์‡  ์•ˆ์— ํƒ€์ž…์„ ๋˜์ ธ ๋„ฃ์—ˆ๋Š”๋ฐ, ๊ทธ๊ฒŒ ๋‹ค ์ œ๋„ค๋ฆญ์ด๋ž€ ๋…€์„์ด์—ˆ๊ตฌ๋‚˜.
๋‚ด์ผ ์ถœ๊ทผํ•˜๋ฉด ๊ทธ๋™์•ˆ ๋ณต๋ถ™ํ•ด ๋†จ๋˜ ApiResponse_OOO ์ธํ„ฐํŽ˜์ด์Šค 40๊ฐœ ์‹น ๋‹ค ์ง€์šฐ๊ณ  ApiResponse<T> ๋กœ ํ†ต์ผํ•˜๋Š” PR ์˜ฌ๋ ค์•ผ๊ฒ ๋‹ค. ๋ฆฌ๋“œ ๋‹˜์ด "์˜ค~ ์˜์ฒ  ๋‹˜ ๋“œ๋””์–ด ๊ฐ ์žก์•˜๋„ค์š”" ํ•˜๊ณ  ์นญ์ฐฌํ•ด์ฃผ๋ฉด ์ข‹๊ฒ ๋‹ค ใ…‹ใ…‹ใ…‹ ํ‡ด๊ทผ ์“ฐ๋ฆฌ, ํˆฌ, ์›! ๋‹ฌ๋ ค!