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

2026๋…„ 4์›” 30์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

ํƒ€์ž…์„ ๋ณ€์ˆ˜์ฒ˜๋Ÿผ ๋„˜๊ฒจ์ฃผ๋Š” ์ œ๋„ค๋ฆญ, ๊ทธ๋ฆฌ๊ณ  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');
  • ๋‹ค๋งŒ ์ด ๋ž˜ํผ๋„ ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ์„ ์ž๋™์œผ๋กœ ํ•ด์ฃผ์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. 1์žฅ์—์„œ ๋ณธ ๊ฒƒ์ฒ˜๋Ÿผ response.json()์˜ ์‹ค์ œ ๋ชจ์–‘์€ ๋„คํŠธ์›Œํฌ ๊ฒฝ๊ณ„์—์„œ ๋ณ„๋„๋กœ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ํ•ต์‹ฌ์€ ๊ณตํ†ต ๋ด‰ํˆฌ์™€ ํ˜ธ์ถœ๋ถ€๋ณ„ ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ด‰ํˆฌ๋Š” ๋ž˜ํผ๊ฐ€ ์ฑ…์ž„์ง€๊ณ , ์ตœ์ข… ๋ฐ์ดํ„ฐ ํƒ€์ž…์€ ํ˜ธ์ถœํ•˜๋Š” ์ชฝ์—์„œ <User>์ฒ˜๋Ÿผ ๋ช…ํ™•ํžˆ ๋„˜๊น๋‹ˆ๋‹ค.

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

์˜ค๋Š˜์€ ์ œ๋„ค๋ฆญ์ด "์–ด๋ ค์šด ๋ฌธ๋ฒ•"์ด๋ผ๊ธฐ๋ณด๋‹ค ํƒ€์ž… ์„ธ๊ณ„์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ผ๋Š” ๊ฒŒ ์„ ๋ช…ํ•ด์กŒ๋‹ค. ApiResponse<T> ํ•˜๋‚˜๋กœ ๊ณตํ†ต ์‘๋‹ต ๋ด‰ํˆฌ๋ฅผ ๊ณ ์ •ํ•˜๊ณ , ์•ˆ์ชฝ data๋งŒ ํ˜ธ์ถœ๋ถ€๋งˆ๋‹ค ๋ฐ”๊พธ๋ฉด ๋ณต๋ถ™ ํƒ€์ž…์ด ์‚ฌ๋ผ์ง„๋‹ค.

extends ์ œ์•ฝ๋„ ๋งˆ์Œ์— ๋‚จ์•˜๋‹ค. ์•„๋ฌด ํƒ€์ž…์ด๋‚˜ ๋ฐ›๋Š” ์ž์œ ๋ณด๋‹ค, ์ตœ์†Œํ•œ์˜ ์กฐ๊ฑด์„ ๊ฑธ์–ด๋‘๋Š” ์ž์œ ๊ฐ€ ๋” ์‹ค๋ฌด์ ์ด์—ˆ๋‹ค. length๊ฐ€ ํ•„์š”ํ•˜๋ฉด T extends { length: number }์ฒ˜๋Ÿผ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ดํ•ดํ•  ๊ทผ๊ฑฐ๋ฅผ ์ค˜์•ผ ํ•œ๋‹ค.

๐Ÿ’ก "์ œ๋„ค๋ฆญ์€ ์ค‘๋ณต ํƒ€์ž…์„ ์ง€์šฐ๋Š” ๋„๊ตฌ์ด์ง€๋งŒ, ์ œ์•ฝ ์—†๋Š” ์ œ๋„ค๋ฆญ์€ ์ฑ…์ž„ ์—†๋Š” ๋นˆ์นธ์ด ๋œ๋‹ค. ๋ณ€ํ•˜๋Š” ๋ถ€๋ถ„๊ณผ ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•œ ์กฐ๊ฑด์„ ํ•จ๊ป˜ ํ‘œํ˜„ํ•ด์•ผ ํ•œ๋‹ค."

๋‚ด์ผ์€ ApiResponse_User์ฒ˜๋Ÿผ ์ด๋ฆ„๋งŒ ๋‹ค๋ฅธ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ApiResponse<TData>๋กœ ์ •๋ฆฌํ•˜๋Š” PR์„ ์ž‘๊ฒŒ ์˜ฌ๋ฆด ์ƒ๊ฐ์ด๋‹ค. ํ•œ ๋ฒˆ์— ์ „๋ถ€ ๋ฐ”๊พธ๊ธฐ๋ณด๋‹ค ํ˜ธ์ถœ๋ถ€ ํƒ€์ž… ์ถ”๋ก ์ด ๊นจ์ง€๋Š” ๊ณณ์„ ํ™•์ธํ•˜๋ฉด์„œ ์ฒœ์ฒœํžˆ ๋ฌถ์–ด์•ผ๊ฒ ๋‹ค.