LHS_EffectiveTypescript_07 - YDP-SPLOUNGE-CLUB/typescript-study GitHub Wiki

์•„์ดํ…œ 31 ํƒ€์ž… ์ฃผ๋ณ€์— null ๊ฐ’ ๋ฐฐ์น˜ํ•˜๊ธฐ

strictNullChecks ์„ค์ •์„ ์ฒ˜์Œ ์ผœ๋ฉด, null์ด๋‚˜ undefined ๊ฐ’ ๊ด€๋ จ๋œ ์˜ค๋ฅ˜๋“ค์ด ๊ฐ‘์ž๊ธฐ ๋‚˜ํƒ€๋‚˜๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋ฅ˜๋ฅผ ๊ฑธ๋Ÿฌ๋‚ด๋Š” if๊ตฌ๋ฌธ์„ ์ฝ”๋“œ ์ „์ฒด์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

์™œ๋ƒํ•˜๋ฉด ์–ด๋–ค ๋ณ€์ˆ˜๊ฐ€ null์ด ๋  ์ˆ˜ ์žˆ๋Š”์ง€ ์—†๋Š”์ง€๋ฅผ ํƒ€์ž…๋งŒ์œผ๋กœ๋Š” ๋ช…ํ™•ํ•˜๊ฒŒ ํ‘œํ˜„ํ•˜๊ธฐ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ฐ’์ด ์ „๋ถ€ null์ด๊ฑฐ๋‚˜ ์ „๋ถ€ null์ด ์•„๋‹Œ ๊ฒฝ์šฐ๋กœ ๋ถ„๋ช…ํžˆ ๊ตฌ๋ถ„๋œ๋‹ค๋ฉด, ๊ฐ’์ด ์„ž์—ฌ์žˆ์„ ๋•Œ๋ณด๋‹ค ๋‹ค๋ฃจ๊ธฐ ์‰ฝ๋‹ค. ํƒ€์ž…์— null์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋ฅผ ๋ชจ๋ธ๋งํ•  ์ˆ˜ ์žˆ๋‹ค.

// tsConfig: {"strictNullChecks":false}  
  
function extent(nums: number[]) {  
  let min, max  
  for (const num of nums) {  
    if (!min) {  
      min = num  
      max = num  
    } else {  
      min = Math.min(min, num)  
      max = Math.max(max, num)  
    }  }  
  return [min, max]  
}

undefined๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฐ์ฒด๋Š” ๋‹ค๋ฃจ๊ธฐ ์–ด๋ ต๊ณ  ์ ˆ๋Œ€ ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด min๊ณผ max๊ฐ€ ๋™์‹œ์— ๋‘˜ ๋‹ค undefined์ด๊ฑฐ๋‚˜ ๋‘˜ ๋‹ค undefined๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์ง€๋งŒ, ์ด๋Ÿฌํ•œ ์ •๋ณด๋Š” ํƒ€์ž…์‹œ์Šคํ…œ์—์„œ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†๋‹ค.

// tsConfig: {"strictNullChecks":true}  
function extent(nums: number[]) {  
  let min, max  
  for (const num of nums) {  
    if (!min) {  
      min = num  
      max = num  
    } else {  
      min = Math.min(min, num)  
      max = Math.max(max, num)  
      // ~~~ Argument of type 'number | undefined' is not  
      //     assignable to parameter of type 'number'    }  
  }  
  return [min, max]  
}

extent์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…์ด (null | undefined)[]๋กœ ์ถ”๋ก ๋˜์–ด์„œ ์„ค๊ณ„์  ๊ฒฐํ•จ์ด ๋ถ„๋ช…ํ•ด์กŒ๋‹ค. ์ด์ œ๋Š” extent๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ณณ๋งˆ๋‹ค ํƒ€์ž… ์˜ค๋ฅ˜์˜ ํ˜•ํƒœ๋กœ ๋‚˜ํƒ€๋‚œ๋‹ค.

ํ•œ ๊ฐ’์˜ null ์—ฌ๋ถ€๊ฐ€ ๋‹ค๋ฅธ ๊ฐ’์˜ null ์—ฌ๋ถ€์— ์•”์‹œ์ ์œผ๋กœ ๊ด€๋ จ๋˜๋„๋ก ์„ค๊ณ„ํ•˜๋ฉด ์•ˆ๋œ๋‹ค.

๋” ๋‚˜์€ ํ•ด๋ฒ•์€ min๊ณผ max๋ฅผ ํ•œ ๊ฐ์ฒด ์•ˆ์— ๋„ฃ๊ณ  null์ด๊ฑฐ๋‚˜ null์ด ์•„๋‹ˆ๊ฒŒ ํ•˜๋ฉด ๋œ๋‹ค.

function extent(nums: number[]) {  
  let result: [number, number] | null = null  
  for (const num of nums) {  
    if (!result) {  
      result = [num, num]  
    } else {  
      result = [Math.min(num, result[0]), Math.max(num, result[1])]  
    }  
  }  
  return result  
}

์ด์ œ๋Š” ๋ฐ˜ํ™˜ ํƒ€์ž…์ด [number, number] | null์ด ๋˜์–ด์„œ ์‚ฌ์šฉํ•˜๊ธฐ๊ฐ€ ๋” ์ˆ˜์›”ํ•ด์กŒ๋‹ค.

const [min, max] = extent([0, 1, 2])!  
const span = max - min // OK

null ์•„๋‹˜ ๋Œ€์‹  ๋‹จ์ˆœ if ๊ตฌ๋ฌธ์œผ๋กœ๋„ ์ฒดํฌํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

const range = extent([0, 1, 2])  
if (range) {  
  const [min, max] = range  
  const span = max - min // OK  
}

extent์˜ ๊ฒฐ๊ณผ๊ฐ’์œผ๋กœ ๋‹จ์ผ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ์„ค๊ณ„๋ฅผ ๊ฐœ์„ ํ–ˆ๊ณ , ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ Null ๊ฐ’ ์‚ฌ์ด์˜ ๊ด€๊ณ„๋ฅผ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์œผ๋ฉฐ ๋ฒ„๊ทธ๋„ ์ œ๊ฑฐํ–ˆ๋‹ค.

API ์ž‘์„ฑ ์‹œ์—๋Š” ๋ฐ˜ํ™˜ ํƒ€์ž…์„ ํฐ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค๊ณ  ๋ฐ˜ํ™˜ ํƒ€์ž… ์ „์ฒด๊ฐ€ null ์ด๊ฑฐ๋‚˜ null์ด ์•„๋‹ˆ๊ฒŒ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค. ์‚ฌ๋žŒ๊ณผ ํƒ€์ž… ์ฒด์ปค ๋ชจ๋‘์—๊ฒŒ ๋ช…๋ฃŒํ•œ ์ฝ”๋“œ๊ฐ€ ๋  ๊ฒƒ์ด๋‹ค.

ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ๋•Œ๋Š” ํ•„์š”ํ•œ ๋ชจ๋“  ๊ฐ’์ด ์ค€๋น„๋˜์—ˆ์„ ๋•Œ ์ƒ์„ฑํ•˜์—ฌ Null ์ด ์กด์žฌํ•˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

strictNullChecks๋ฅผ ์„ค์ •ํ•˜๋ฉด ์ฝ”๋“œ์— ๋งŽ์€ ์˜ค๋ฅ˜๊ฐ€ ํ‘œ์‹œ๋˜๊ฒ ์ง€๋งŒ, null ๊ฐ’๊ณผ ๊ด€๋ จ๋œ ๋ฌธ์ œ์ ์„ ์ฐพ์•„๋‚ผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•˜๋‹ค.

์•„์ดํ…œ 32 ์œ ๋‹ˆ์˜จ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ณด๋‹ค๋Š” ์ธํ„ฐํŽ˜์ด์Šค์˜ ์œ ๋‹ˆ์˜จ์„ ์‚ฌ์šฉํ•˜๊ธฐ

์œ ๋‹ˆ์˜จ ํƒ€์ž…์˜ ์†์„ฑ์„ ์—ฌ๋Ÿฌ ๊ฐœ ๊ฐ€์ง€๋Š” ์ธํ„ฐํŽ˜์ด์Šค์—์„œ๋Š” ์†์„ฑ ๊ฐ„์˜ ๊ด€๊ณ„๊ฐ€ ๋ถ„๋ช…ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ˆ˜๊ฐ€ ์ž์ฃผ ๋ฐœ์ƒํ•˜๋ฏ€๋กœ ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.

์œ ๋‹ˆ์˜จ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ณด๋‹ค ์ธํ„ฐํŽ˜์ด์Šค์˜ ์œ ๋‹ˆ์˜จ์ด ๋” ์ •ํ™•ํ•˜๊ณ  ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ดํ•ดํ•˜๊ธฐ๋„ ์ข‹๋‹ค.

๋ฒกํ„ฐ๋ฅผ ๊ทธ๋ฆฌ๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ์ž‘์„ฑ์ค‘์ด๊ณ , ๊ธฐํ•˜ํ•™์ ํƒ€์ž…์„ ๊ฐ€์ง€๋Š” ๊ณ„์ธต์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณด์ž.

type FillPaint = unknown  
type LinePaint = unknown  
type PointPaint = unknown  
type FillLayout = unknown  
type LineLayout = unknown  
type PointLayout = unknown  

interface Layer {  
  layout: FillLayout | LineLayout | PointLayout  
  paint: FillPaint | LinePaint | PointPaint  
}

์ด๋Ÿฐ ์กฐํ•ฉ์„ ํ—ˆ์šฉํ•œ๋‹ค๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‹ญ์ƒ์ด๊ณ  ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋‹ค๋ฃจ๊ธฐ๋„ ์–ด๋ ค์šธ ๊ฒƒ์ด๋‹ค.

๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์œผ๋กœ ๋ชจ๋ธ๋งํ•˜๋ ค๋ฉด ๊ฐ๊ฐ ํƒ€์ž…์˜ ๊ณ„์ธต์„ ๋ถ„๋ฆฌ๋œ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋‘ฌ์•ผ ํ•œ๋‹ค.

interface FillLayer {  
  layout: FillLayout  
  paint: FillPaint  
}  
interface LineLayer {  
  layout: LineLayout  
  paint: LinePaint  
}  
interface PointLayer {  
  layout: PointLayout  
  paint: PointPaint  
}  
type Layer = FillLayer | LineLayer | PointLayer

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ œ์–ด ํ๋ฆ„์„ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ๋„๋ก ํƒ€์ž…์— ํƒœ๊ทธ๋ฅผ ๋„ฃ๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค. ํƒœ๊ทธ๋œ ์œ ๋‹ˆ์˜จ์€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์™€ ๋งค์šฐ ์ž˜ ๋งž๊ธฐ ๋•Œ๋ฌธ์— ์ž์ฃผ ๋ณผ ์ˆ˜ ์žˆ๋Š” ํŒจํ„ด์ด๋‹ค.

๋˜ ๋‹ค๋ฅธ ์˜ˆ์‹œ๋Š” ํƒœ๊ทธ๋œ ์œ ๋‹ˆ์˜จ์„ ์˜ˆ๋กœ ๋“ค ์ˆ˜ ์žˆ๋‹ค.

interface Layer {  
  type: 'fill' | 'line' | 'point'  
  layout: FillLayout | LineLayout | PointLayout  
  paint: FillPaint | LinePaint | PointPaint  
}

type: 'fill' ๊ณผ ํ•จ๊ป˜ LineLaouy๊ณผ PointPaint ํƒ€์ž…์ด ์“ฐ์ด๋Š” ๊ฒƒ์€ ๋ง์ด ๋˜์ง€ ์•Š๋Š”๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด Layer๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค์˜ ์œ ๋‹ˆ์˜จ์œผ๋กœ ๋ณ€ํ™˜ํ•ด๋ณด์ž.

interface FillLayer {  
  type: 'fill'  
  layout: FillLayout  
  paint: FillPaint  
}  
interface LineLayer {  
  type: 'line'  
  layout: LineLayout  
  paint: LinePaint  
}  
interface PointLayer {  
  type: 'paint'  
  layout: PointLayout  
  paint: PointPaint  
}  
type Layer = FillLayer | LineLayer | PointLayer
type Layer = FillLayer | LineLayer | PointLayer  
function drawLayer(layer: Layer) {  
  if (layer.type === 'fill') {  
    const { paint } = layer // Type is FillPaint  
    const { layout } = layer // Type is FillLayout  
  } else if (layer.type === 'line') {  
    const { paint } = layer // Type is LinePaint  
    const { layout } = layer // Type is LineLayout  
  } else {  
    const { paint } = layer // Type is PointPaint  
    const { layout } = layer // Type is PointLayout  
  }  
}

๊ฐ ํƒ€์ž…์˜ ์†์„ฑ๋“ค ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ์ œ๋Œ€๋กœ ๋ชจ๋ธ๋งํ•˜๋ฉด ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ฝ”๋“œ์˜ ์ •ํ™•์„ฑ์„ ์ฒดํฌํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋œ๋‹ค. ๋‹ค๋งŒ ํƒ€์ž… ๋ถ„๊ธฐ ํ›„ layer๊ฐ€ ํฌํ•จ๋œ ๋™์ผํ•œ ์ฝ”๋“œ๊ฐ€ ๋ฐ˜๋ณต๋˜๋Š” ๊ฒƒ์ด ์–ด์ˆ˜์„ ํ•ด ๋ณด์ธ๋‹ค.

ํƒœ๊ทธ๋œ ์œ ๋‹ˆ์˜จ์€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์™€ ๋งค์šฐ ์ž˜ ๋งž๊ธฐ๋•Œ๋ฌธ์— ํ•„์š”ํ•  ๋•Œ ์ ์šฉํ•˜๋„๋กํ•˜์ž.

์•„์ดํ…œ 33 string ํƒ€์ž…๋ณด๋‹ค ๋” ๊ตฌ์ฒด์ ์ธ ํƒ€์ž… ์‚ฌ์šฉํ•˜๊ธฐ

'๋ฌธ์ž์—ด์„ ๋‚จ๋ฐœํ•˜์—ฌ ์„ ์–ธ๋œ' ์ฝ”๋“œ๋ฅผ ํ”ผํ•˜๋„๋กํ•˜์ž. ๋ชจ๋“  ๋ฌธ์ž์—ด์„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋Š” string ํƒ€์ž…๋ณด๋‹ค๋Š” ๋” ๊ตฌ์ฒด์ ์ธ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

string ํƒ€์ž…์˜ ๋ฒ”์œ„๋Š” ๋งค์šฐ ๋„“๋‹ค.

interface Album {  
  artist: string  
  title: string  
  releaseDate: string // YYYY-MM-DD  
  recordingType: string // E.g., "live" or "studio"  
}  

๋ณ€์ˆ˜์˜ ๋ฒ”์œ„๋ฅผ ๋ณด๋‹ค ์ •ํ™•ํ•˜๊ฒŒ ํ‘œํ˜„ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด string ํƒ€์ž…๋ณด๋‹ค๋Š” ๋ฌธ์ž์—ด ๋ฆฌํ„ฐ๋Ÿด ํƒ€์ž…์˜ ์œ ๋‹ˆ์˜จ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ํƒ€์ž… ์ฒด์ปค๋ฅผ ๋” ์—„๊ฒฉํžˆ ํ•  ์ˆ˜ ์žˆ๊ณ  ์ƒ์‚ฐ์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

releaseDate ํ•„๋“œ๋Š” Date ๊ฐ์ฒด๋กœ ์‚ฌ์šฉํ•ด์„œ ๋‚ ์งœ ํ˜•์‹์œผ๋กœ๋งŒ ์ œํ•œํ•˜๋Š”๊ฒƒ์ด ์ข‹๋‹ค. recordingType ํ•„๋“œ๋Š” 'live'์™€ 'studio' ๋‹จ ๋‘ ๊ฐœ์˜ ๊ฐ’์œผ๋กœ ์œ ๋‹ˆ์˜จ ํƒ€์ž…์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

type RecordingType = 'studio' | 'live'  
  
interface Album {  
  artist: string  
  title: string  
  releaseDate: Date  
  recordingType: RecordingType  
}

๊ฐ์ฒด์˜ ์†์„ฑ ์ด๋ฆ„์„ ํ•จ์ˆ˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์„ ๋•Œ๋Š” string๋ณด๋‹ค keyof T๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

function pluck<T, K extends keyof T>(record: T[], key: K): T[K][] {  
  return record.map(r => r[key])  
}
pluck(albums, 'releaseDate') // Type is Date[]  
pluck(albums, 'artist') // Type is string[]  
pluck(albums, 'recordingType') // Type is RecordingType[]  
pluck(albums, 'recordingDate')  
// ~~~~~~~~~~~~~~~ Argument of type '"recordingDate"' is not  
//                 assignable to parameter of type ...

์ด์™€๊ฐ™์ด keyof T ๋ถ€๋ถ„์ง‘ํ•ฉ์„ ํ™œ์šฉํ•˜์—ฌ ๊ฐ์ฒด์˜ ์†์„ฑ์„ ์ œ๋Œ€๋กœ ๋ฐ˜ํ™˜ํƒ€์ž…์ด ์ถ”๋ก ๋  ์ˆ˜ ์žˆ๋‹ค.

๋ณด๋‹ค ์ •ํ™•ํ•œ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๋ฉด ์˜ค๋ฅ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ  ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๋„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

์•„์ดํ…œ 34 ๋ถ€์ •ํ™•ํ•œ ํƒ€์ž…๋ณด๋‹ค๋Š” ๋ฏธ์™„์„ฑ ํƒ€์ž…์„ ์‚ฌ์šฉํ•˜๊ธฐ

ํƒ€์ž… ์•ˆ์ „์„ฑ์—์„œ ๋ถˆ์พŒํ•œ ๊ณจ์งœ๊ธฐ๋Š” ํ”ผํ•ด์•ผํ•œ๋‹ค. ํƒ€์ž…์ด ์—†๋Š” ๊ฒƒ๋ณด๋‹ค ์ž˜๋ชป๋œ๊ฒŒ ๋” ๋‚˜์˜๋‹ค.

๋ถˆ์พŒํ•œ ๊ณจ์งœ๊ธฐ

๋กœ๋ด‡ ๊ณตํ•™๊ณผ ์ธ๊ณต ์ง€๋Šฅ์—์„œ ๋งŽ์ด ์“ฐ์ด๋Š” ์šฉ์–ด ์–ด์„คํ”„๊ฒŒ ์ธ๊ฐ„๊ณผ ๋น„์Šทํ•œ ๋กœ๋ด‡์—์„œ ๋А๋ผ๋Š” ๋ถˆ์พŒํ•จ์„ ๋œปํ•œ๋‹ค. ์ €์ž์˜ ์˜๋„๋Š” ํƒ€์ž… ์„ ์–ธ์—์„œ ์–ด์„คํ”„๊ฒŒ ์™„๋ฒฝ์„ ์ถ”๊ตฌํ•˜๋ ค๋‹ค๊ฐ€ ์˜คํžˆ๋ ค ์—ญํšจ๊ณผ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์„ ์ฃผ์˜ํ•˜์ž๋Š” ์˜๋ฏธ์ด๋‹ค.

ํƒ€์ž… ์„ ์–ธ์„ ์ž‘์„ฑํ•˜๋‹ค ๋ณด๋ฉด ์ฝ”๋“œ์˜ ๋™์ž‘์„ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ ๋˜๋Š” ๋œ ๊ตฌ์ฒด์ ์œผ๋กœ ๋ชจ๋ธ๋งํ•˜๊ฒŒ ๋˜๋Š” ์ƒํ™ฉ์„ ๋งž๋‹ฅ๋œจ๋ฆฌ๊ฒŒ ๋œ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ ํƒ€์ž…์ด ๊ตฌ์ฒด์ ์ผ์ˆ˜๋ก ๋ฒ„๊ทธ๋ฅผ ๋” ๋งŽ์ด ์žก๊ณ  ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋„๊ตฌ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ํƒ€์ž… ์„ ์–ธ์˜ ์ •๋ฐ€๋„๋ฅผ ๋†’์ด๋Š” ์ผ์—๋Š” ์ฃผ์˜๋ฅผ ๊ธฐ์šธ์—ฌ์•ผ ํ•œ๋‹ค.

์˜ˆ๋ฅผ๋“ค์–ด ๋‹ค์Œ์˜ GeoJSON์€ ๊ฐ๊ฐ ๋‹ค๋ฅธ ํ˜•ํƒœ์˜ ์ขŒํ‘œ ๋ฐฐ์—ด์„ ๊ฐ€์ง€๋Š” ํƒ€์ž…์ด๋‹ค.

interface Point {  
  type: 'Point'  
  coordinates: number[]  
}  
interface LineString {  
  type: 'LineString'  
  coordinates: number[][]  
}  
interface Polygon {  
  type: 'Polygon'  
  coordinates: number[][][]  
}  
type Geometry = Point | LineString | Polygon // ๋‹ค๋ฅธ ๊ฒƒ๋“ค๋„ ์ถ”๊ฐ€๋  ์ˆ˜ ์žˆ๋‹ค.

ํฐ ๋ฌธ์ œ๋Š” ์—†์ง€๋งŒ ์ขŒํ‘œ์— ์“ฐ์ด๋Š” number[]๊ฐ€ ์•ฝ๊ฐ„ ์ถ”์ƒ์ ์ด๋‹ค. ์—ฌ๊ธฐ์„œ number[]๋Š” ๊ฒฝ๋„์™€ ์œ„๋„๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฏ€๋กœ ํŠœํ”Œ ํƒ€์ž…์œผ๋กœ ์„ ์–ธํ•˜๋Š”๊ฒŒ ๋‚ซ๋‹ค.

type GeoPosition = [number, number]  
interface Point {  
  type: 'Point'  
  coordinates: GeoPosition  
}  
// Etc.

ํƒ€์ž…์„ ๋” ๊ตฌ์ฒด์ ์œผ๋กœ ๊ฐœ์„ ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋” ๋‚˜์€ ์ฝ”๋“œ๊ฐ€ ๋œ ๊ฒƒ ๊ฐ™๋‹ค.

์ปค๋ฎค๋‹ˆํ‹ฐ์— ์ž๋ž‘ํ•˜์—ฌ ์ข‹์•„์š”๋ฅผ ๊ธฐ๋Œ€ํ–ˆ์ง€๋งŒ ์•ˆํƒ€๊น๊ฒŒ๋„ ์ƒˆ๋กœ์šด ์ฝ”๋“œ๊ฐ€ ๋นŒ๋“œ๋ฅผ ๊นจ๋œจ๋ฆฐ๋‹ค๋ฉฐ ๋ถˆํ‰ํ•˜๋Š” ์‚ฌ์šฉ์ž๋“ค์˜ ๋ชจ์Šต๋งŒ ๋ณด๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

์ฝ”๋“œ์—๋Š” ์œ„๋„์™€ ๊ฒฝ๋„๋งŒ์„ ๋ช…์‹œํ–ˆ์ง€๋งŒ, GeoJSON์˜ ์œ„์น˜ ์ •๋ณด์—๋Š” ์„ธ๋ฒˆ์งธ ์š”์†Œ์ธ ๊ณ ๋„๊ฐ€ ์žˆ์„ ์ˆ˜๋„ ์žˆ๊ณ  ๋˜ ๋‹ค๋ฅธ ์ •๋ณด๊ฐ€ ์žˆ์„ ์ˆ˜๋„ ์žˆ๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ ํƒ€์ž… ์„ ์–ธ์„ ์„ธ๋ฐ€ํ•˜๊ฒŒ ๋งŒ๋“ค๊ณ ์ž ํ–ˆ์ง€๋งŒ ์‹œ๋„๊ฐ€ ๋„ˆ๋ฌด ๊ณผํ–ˆ๊ณ  ์˜คํžˆ๋ ค ํƒ€์ž…์ด ๋ถ€์ •ํ™•ํ•ด์กŒ๋‹ค.

์ •ํ™•ํ•˜๊ฒŒ ํƒ€์ž…์„ ๋ชจ๋ธ๋งํ•  ์ˆ˜ ์—†๋‹ค๋ฉด ๋ถ€์ •ํ™•ํ•˜๊ฒŒ ๋ชจ๋ธ๋งํ•˜์ง€ ๋ง์•„์•ผ ํ•œ๋‹ค. ๋˜ํ•œ any์™€ unknown๋ฅผ ๊ตฌ๋ณ„ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

ํƒ€์ž… ์ •๋ณด๋ฅผ ๊ตฌ์ฒด์ ์œผ๋กœ ๋งŒ๋“ค์ˆ˜๋ก ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€์™€ ์ž๋™์™„์„ฑ ๊ธฐ๋Šฅ์— ์ฃผ์˜๋ฅผ ๊ธฐ์šธ์—ฌ์•ผ ํ•œ๋‹ค. ์ •ํ™•๋„๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ฐœ๋ฐœ ๊ฒฝํ—˜๊ณผ๋„ ๊ด€๋ จ๋œ๋‹ค.

์•„์ดํ…œ 35 ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹Œ, API์™€ ๋ช…์„ธ๋ฅผ ๋ณด๊ณ  ํƒ€์ž… ๋งŒ๋“ค๊ธฐ

์ฝ”๋“œ์˜ ๊ตฌ์„ ๊ตฌ์„๊นŒ์ง€ ํƒ€์ž… ์•ˆ์ •์„ฑ์„ ์–ป๊ธฐ ์œ„ํ•ด API ๋˜๋Š” ๋ฐ์ดํ„ฐ ํ˜•์‹์— ๋Œ€ํ•œ ํƒ€์ž… ์ƒ์„ฑ์„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.

๋ฐ์ดํ„ฐ์— ๋“œ๋Ÿฌ๋‚˜์ง€ ์•Š๋Š” ์˜ˆ์™ธ์ ์ธ ๊ฒฝ์šฐ๋“ค์ด ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ๋ณด๋‹ค๋Š” ๋ช…์„ธ๋กœ๋ถ€ํ„ฐ ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

ํŒŒ์ผํ˜•์‹, API, ๋ช…์„ธ ๋“ฑ ์šฐ๋ฆฌ๊ฐ€ ๋‹ค๋ฃจ๋Š” ํƒ€์ž… ์ค‘ ์ตœ์†Œํ•œ ๋ช‡ ๊ฐœ๋Š” ํ”„๋กœ์ ํŠธ ์™ธ๋ถ€์—์„œ ๋น„๋กฏ๋œ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์—๋Š” ํƒ€์ž…์„ ์ง์ ‘ ์žฅ์„ฑํ•˜์ง€ ์•Š๊ณ  ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•ต์‹ฌ์€ ์˜ˆ์‹œ ๋ฐ์ดํ„ฐ๊ฐ€์•„๋‹ˆ๋ผ ๋ช…์„ธ๋ฅผ ์ฐธ๊ณ ํ•ด ํƒ€์ž…์„ ์ƒ์„ฑํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๋ช…์„ธ๋ฅผ ์ฐธ๊ณ ํ•ด ํƒ€์ž…์„ ์ƒ์„ฑํ•˜๋ฉด ๋ˆˆ์•ž์— ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋“ค๋งŒ ๊ณ ๋ คํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๊ณณ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

interface BoundingBox {  
  lat: [number, number]  
  lng: [number, number]  
}  
type GeoJSONFeature = any  
function calculateBoundingBox(f: GeoJSONFeature): BoundingBox | null {  
  let box: BoundingBox | null = null  
  
  const helper = (coords: any[]) => {  
    // ...  
  }  
  
  const { geometry } = f  
  if (geometry) {  
    helper(geometry.coordinates)  
  }  
  return box  
}

Feature ํƒ€์ž…์€ ๋ช…์‹œ์ ์œผ๋กœ ์ •์˜๋œ ์ ์ด ์—†๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๊ณต์‹ GeoJSON ๋ช…์„ธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚ซ๋‹ค. ๋‹คํ–‰์ด๋„ DefinitelyTyped์—๋Š” ์ด๋ฏธ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํƒ€์ž… ์„ ์–ธ์ด ์กด์žฌํ•œ๋‹ค.

$npm install --save-dev @types/geojson
# @types/[email protected]

GeoJSON ์„ ์–ธ์„ ๋„ฃ๋Š” ์ˆœ๊ฐ„, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋Š” ์˜ค๋ฅ˜๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

// requires node modules: @types/geojson  
  
interface BoundingBox {  
  lat: [number, number]  
  lng: [number, number]  
}  
import { Feature } from 'geojson'  
  
function calculateBoundingBox(f: Feature): BoundingBox | null {  
  let box: BoundingBox | null = null  
  
  const helper = (coords: any[]) => {  
    // ...  
  }  
  
  const { geometry } = f  
  if (geometry) {  
    helper(geometry.coordinates)  
    // ~~~~~~~~~~~  
    // Property 'coordinates' does not exist on type 'Geometry'    //   Property 'coordinates' does not exist on type    //   'GeometryCollection'  }  
  
  return box  
}

API ํ˜ธ์ถœ์—๋„ ๋น„์Šทํ•œ ๊ณ ๋ ค์‚ฌํ•ญ๋“ค์ด ์ ์šฉ๋œ๋‹ค. API์˜ ๋ช…์„ธ๋กœ๋ถ€ํ„ฐ ํƒ€์ž…์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ๊ทธ๋ ‡๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

๋งŒ์•ฝ ๋ช…์„ธ ์ •๋ณด๋‚˜ ๊ณต์‹ ์Šคํ‚ค๋งˆ๊ฐ€ ์—†๋‹ค๋ฉด ๋ฐ์ดํ„ฐ๋กœ๋ถ€ํ„ฐ ํƒ€์ž…์„ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด quicktype ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ƒ์„ฑ๋œ ํƒ€์ž…์ด ์‹ค์ œ ๋ฐ์ดํ„ฐ์™€ ์ผ์น˜ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์„ ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค.