์ง€๋„ ๋ทฐํฌํŠธ ๊ธฐ๋ฐ˜ ์‹ค์‹œ๊ฐ„ ์ˆ™์†Œ ๊ฒ€์ƒ‰ ๊ตฌํ˜„

2026. 1. 14. 21:53ยท๐Ÿ’™ ํ”„๋ก ํŠธ์—”๋“œ(FE)
728x90

์ง€๋„ ๋ทฐํฌํŠธ ๊ธฐ๋ฐ˜ ์‹ค์‹œ๊ฐ„ ์ˆ™์†Œ ๊ฒ€์ƒ‰ ๊ตฌํ˜„

๐Ÿ“‹ ๊ฐœ์š”

์‚ฌ์šฉ์ž๊ฐ€ ์ง€๋„๋ฅผ ์ด๋™ํ•˜๊ฑฐ๋‚˜ ํ™•๋Œ€/์ถ•์†Œํ•  ๋•Œ, ํ˜„์žฌ ํ™”๋ฉด์— ๋ณด์ด๋Š” ์˜์—ญ(๋ทฐํฌํŠธ) ๋‚ด์˜ ์ˆ™์†Œ๋งŒ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฒ€์ƒ‰ํ•˜์—ฌ ํ‘œ์‹œํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

Airbnb์™€ ๊ฐ™์€ ์ง€๋„ ๊ธฐ๋ฐ˜ ๊ฒ€์ƒ‰ ์„œ๋น„์Šค์—์„œ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ํŒจํ„ด์œผ๋กœ, ์‚ฌ์šฉ์ž๊ฐ€ ๊ด€์‹ฌ ์žˆ๋Š” ์ง€์—ญ์˜ ์ˆ™์†Œ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐ŸŽฏ ํ•ต์‹ฌ ๊ฐœ๋…

1. ๋ทฐํฌํŠธ(Viewport)๋ž€?

๋ทฐํฌํŠธ๋Š” ์ง€๋„์—์„œ ํ˜„์žฌ ํ™”๋ฉด์— ๋ณด์ด๋Š” ์‚ฌ๊ฐํ˜• ์˜์—ญ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

          ๋ถ(North)
            ↑
            |
์„œ(West) ←--+--→ ๋™(East)
            |
            ↓
          ๋‚จ(South)

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” ← NE (๋ถ๋™์ชฝ) ๋ชจ์„œ๋ฆฌ
โ”‚                 โ”‚   ์ขŒํ‘œ: (neLat, neLng)
โ”‚   ์ง€๋„ ํ™”๋ฉด     โ”‚
โ”‚   (๋ทฐํฌํŠธ)      โ”‚
โ”‚                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ← SW (๋‚จ์„œ์ชฝ) ๋ชจ์„œ๋ฆฌ
↑                    ์ขŒํ‘œ: (swLat, swLng)

2. Bounds (๊ฒฝ๊ณ„) ์ขŒํ‘œ

๋ทฐํฌํŠธ๋Š” ๋‘ ๊ฐœ์˜ ๋ชจ์„œ๋ฆฌ ์ขŒํ‘œ๋กœ ์™„๋ฒฝํ•˜๊ฒŒ ์ •์˜๋ฉ๋‹ˆ๋‹ค:

  • SW (SouthWest): ๋‚จ์„œ์ชฝ ๋ชจ์„œ๋ฆฌ - ์™ผ์ชฝ ์•„๋ž˜ ๋
    • swLat: ๋‚จ์„œ์ชฝ ์œ„๋„ (๊ฐ€์žฅ ๋‚ฎ์€ ์œ„๋„)
    • swLng: ๋‚จ์„œ์ชฝ ๊ฒฝ๋„ (๊ฐ€์žฅ ๋‚ฎ์€ ๊ฒฝ๋„)
  • NE (NorthEast): ๋ถ๋™์ชฝ ๋ชจ์„œ๋ฆฌ - ์˜ค๋ฅธ์ชฝ ์œ„ ๋
    • neLat: ๋ถ๋™์ชฝ ์œ„๋„ (๊ฐ€์žฅ ๋†’์€ ์œ„๋„)
    • neLng: ๋ถ๋™์ชฝ ๊ฒฝ๋„ (๊ฐ€์žฅ ๋†’์€ ๊ฒฝ๋„)

์˜ˆ์‹œ: ์„œ์šธ ์‹œ๋‚ด ๋ทฐํฌํŠธ

{
  swLat: 37.50,  // ๋‚จ์ชฝ ๊ฒฝ๊ณ„
  swLng: 126.95, // ์„œ์ชฝ ๊ฒฝ๊ณ„
  neLat: 37.60,  // ๋ถ์ชฝ ๊ฒฝ๊ณ„
  neLng: 127.05  // ๋™์ชฝ ๊ฒฝ๊ณ„
}

๐Ÿ—๏ธ ์•„ํ‚คํ…์ฒ˜

์ „์ฒด ํ๋ฆ„๋„

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                         ์‚ฌ์šฉ์ž ์•ก์…˜                          โ”‚
โ”‚            (์ง€๋„ ์ด๋™, ํ™•๋Œ€/์ถ•์†Œ, ๋“œ๋ž˜๊ทธ)                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
                         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    MapView ์ปดํฌ๋„ŒํŠธ                          โ”‚
โ”‚  - ์นด์นด์˜ค๋งต API์—์„œ bounds ์ •๋ณด ํš๋“                        โ”‚
โ”‚  - bounds_changed ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋‹                             โ”‚
โ”‚  - ๋””๋ฐ”์šด์‹ฑ ์—†์ด ์ฆ‰์‹œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌ                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
                         โ”‚ onMapBoundsChange({ swLat, swLng, neLat, neLng })
                         โ”‚
                         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                SearchResultPage ์ปดํฌ๋„ŒํŠธ                     โ”‚
โ”‚  - handleMapBoundsChange์—์„œ bounds ์ˆ˜์‹                     โ”‚
โ”‚  - 500ms ๋””๋ฐ”์šด์‹ฑ ์ ์šฉ (๊ณผ๋„ํ•œ API ํ˜ธ์ถœ ๋ฐฉ์ง€)               โ”‚
โ”‚  - SearchParams ๊ตฌ์„ฑ (๊ธฐ์กด ํ•„ํ„ฐ + bounds)                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
                         โ”‚ searchAccommodations(params)
                         โ”‚
                         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  Backend API Server                          โ”‚
โ”‚  GET /accommodations?swLat=...&swLng=...&neLat=...&neLng=...โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
                         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              AccommodationService (NestJS)                   โ”‚
โ”‚  - bounds ํŒŒ๋ผ๋ฏธํ„ฐ ์šฐ์„  ๊ฒ€์‚ฌ                                โ”‚
โ”‚  - SQL WHERE ์ ˆ๋กœ ์‚ฌ๊ฐํ˜• ์˜์—ญ ํ•„ํ„ฐ๋ง                        โ”‚
โ”‚  - ๋ทฐํฌํŠธ ๋‚ด ์ˆ™์†Œ๋งŒ ๋ฐ˜ํ™˜                                    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
                         โ”‚ { items: [...], total, page, ... }
                         โ”‚
                         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    ํ™”๋ฉด์— ๊ฒฐ๊ณผ ํ‘œ์‹œ                          โ”‚
โ”‚  - ์ขŒ์ธก: ์ˆ™์†Œ ๋ฆฌ์ŠคํŠธ ์—…๋ฐ์ดํŠธ                               โ”‚
โ”‚  - ์šฐ์ธก: ์ง€๋„ ๋งˆ์ปค ์—…๋ฐ์ดํŠธ                                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ’ป ๊ตฌํ˜„ ์ƒ์„ธ

1. ํ”„๋ก ํŠธ์—”๋“œ: MapView ์ปดํฌ๋„ŒํŠธ

์—ญํ• 

  • ์นด์นด์˜ค๋งต API์—์„œ ๋ทฐํฌํŠธ bounds ์ •๋ณด ์ถ”์ถœ
  • ์ง€๋„ ์ด๋™/์คŒ ๋ณ€๊ฒฝ ์‹œ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ bounds ์ „๋‹ฌ

ํ•ต์‹ฌ ์ฝ”๋“œ

// frontend/src/components/map/MapView.tsx

// bounds_changed ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋‹
useEffect(() => {
  if (!map) return;

  const updateBounds = () => {
    // ์นด์นด์˜ค๋งต์—์„œ ํ˜„์žฌ ๋ทฐํฌํŠธ์˜ ๊ฒฝ๊ณ„ ์ขŒํ‘œ ํš๋“
    const mapBounds = map.getBounds();
    const sw = mapBounds.getSouthWest(); // ๋‚จ์„œ์ชฝ ๋ชจ์„œ๋ฆฌ
    const ne = mapBounds.getNorthEast(); // ๋ถ๋™์ชฝ ๋ชจ์„œ๋ฆฌ

    const newBounds = {
      swLat: sw.getLat(),  // ๋‚จ์„œ์ชฝ ์œ„๋„
      swLng: sw.getLng(),  // ๋‚จ์„œ์ชฝ ๊ฒฝ๋„
      neLat: ne.getLat(),  // ๋ถ๋™์ชฝ ์œ„๋„
      neLng: ne.getLng(),  // ๋ถ๋™์ชฝ ๊ฒฝ๋„
    };

    // ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋กœ bounds ์ „๋‹ฌ
    onMapBoundsChange?.(newBounds);
  };

  // bounds_changed ์ด๋ฒคํŠธ ๋“ฑ๋ก
  // (zoom_changed, dragend ๋ฐœ์ƒ ์‹œ์—๋„ ์ž๋™์œผ๋กœ ํŠธ๋ฆฌ๊ฑฐ๋จ)
  kakao.maps.event.addListener(map, 'bounds_changed', updateBounds);

  return () => {
    kakao.maps.event.removeListener(map, 'bounds_changed', updateBounds);
  };
}, [map, onMapBoundsChange]);

์ฃผ์š” ํฌ์ธํŠธ

  1. getBounds() ๋ฉ”์„œ๋“œ: ์นด์นด์˜ค๋งต API๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํ˜„์žฌ ๋ทฐํฌํŠธ ์ •๋ณด
  2. bounds_changed ์ด๋ฒคํŠธ: ์ง€๋„ ์ด๋™/์คŒ ๋ณ€๊ฒฝ ์‹œ ์ž๋™ ๋ฐœ์ƒ
  3. ์ฆ‰์‹œ ์ „๋‹ฌ: MapView์—์„œ๋Š” ๋””๋ฐ”์šด์‹ฑ ์—†์ด ์ฆ‰์‹œ ๋ถ€๋ชจ๋กœ ์ „๋‹ฌ

2. ํ”„๋ก ํŠธ์—”๋“œ: SearchResultPage ์ปดํฌ๋„ŒํŠธ

์—ญํ• 

  • MapView๋กœ๋ถ€ํ„ฐ bounds ์ˆ˜์‹ 
  • ๋””๋ฐ”์šด์‹ฑ ์ ์šฉํ•˜์—ฌ ๊ณผ๋„ํ•œ API ํ˜ธ์ถœ ๋ฐฉ์ง€
  • ๊ธฐ์กด ๊ฒ€์ƒ‰ ํ•„ํ„ฐ + bounds๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ API ํ˜ธ์ถœ

ํ•ต์‹ฌ ์ฝ”๋“œ

// frontend/src/pages/SearchResultPage.tsx

const handleMapBoundsChange = useCallback(
  (bounds: {
    swLat: number;
    swLng: number;
    neLat: number;
    neLng: number;
  }) => {
    // ๊ธฐ์กด ํƒ€์ด๋จธ ์ทจ์†Œ
    if (debounceTimerRef.current) {
      clearTimeout(debounceTimerRef.current);
    }

    // 500ms ํ›„์— API ํ˜ธ์ถœ (๋””๋ฐ”์šด์‹ฑ)
    debounceTimerRef.current = window.setTimeout(async () => {
      setIsLoading(true);
      setError(null);

      try {
        // ๊ธฐ์กด ๊ฒ€์ƒ‰ ํŒŒ๋ผ๋ฏธํ„ฐ ์œ ์ง€ + bounds ์ถ”๊ฐ€
        const params = parseSearchParams();
        params.swLat = bounds.swLat;
        params.swLng = bounds.swLng;
        params.neLat = bounds.neLat;
        params.neLng = bounds.neLng;

        const response = await searchAccommodations(params);
        const convertedData = response.items.map(convertToAccommodation);
        setAccommodations(convertedData);
      } catch (err) {
        console.error('์ง€๋„ ์ด๋™ ์ค‘ ์ˆ™์†Œ ๊ฒ€์ƒ‰ ์˜ค๋ฅ˜:', err);
      } finally {
        setIsLoading(false);
      }
    }, 500);
  },
  [parseSearchParams]
);

์ฃผ์š” ํฌ์ธํŠธ

  1. ๋””๋ฐ”์šด์‹ฑ (500ms): ์‚ฌ์šฉ์ž๊ฐ€ ์ง€๋„๋ฅผ ๊ณ„์† ๋“œ๋ž˜๊ทธํ•˜๋Š” ๋™์•ˆ API ํ˜ธ์ถœ ๋ฐฉ์ง€
  2. ๊ธฐ์กด ํ•„ํ„ฐ ์œ ์ง€: ๋‚ ์งœ, ๊ฐ€๊ฒฉ, ์ธ์› ๋“ฑ ๊ธฐ์กด ๊ฒ€์ƒ‰ ์กฐ๊ฑด ์œ ์ง€
  3. ์—๋Ÿฌ ์ฒ˜๋ฆฌ: ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ๊ธฐ์กด ๋ชฉ๋ก ์œ ์ง€ (UX ํ–ฅ์ƒ)

3. ๋ฐฑ์—”๋“œ: DTO (Data Transfer Object)

์—ญํ• 

  • API ์š”์ฒญ ํŒŒ๋ผ๋ฏธํ„ฐ ์ •์˜ ๋ฐ ์œ ํšจ์„ฑ ๊ฒ€์ฆ

ํ•ต์‹ฌ ์ฝ”๋“œ

// backend/src/modules/accommodation/dto/search-accommodation.dto.ts

export class SearchAccommodationDto {
  // ์ง€๋„ ๋ทฐํฌํŠธ ๊ธฐ๋ฐ˜ ๊ฒ€์ƒ‰ (์‚ฌ๊ฐํ˜• ์˜์—ญ)
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  swLat?: number; // ๋‚จ์„œ์ชฝ ์œ„๋„

  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  swLng?: number; // ๋‚จ์„œ์ชฝ ๊ฒฝ๋„

  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  neLat?: number; // ๋ถ๋™์ชฝ ์œ„๋„

  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  neLng?: number; // ๋ถ๋™์ชฝ ๊ฒฝ๋„

  // ... ๊ธฐํƒ€ ํ•„ํ„ฐ (๋‚ ์งœ, ๊ฐ€๊ฒฉ, ์ธ์› ๋“ฑ)
}

4. ๋ฐฑ์—”๋“œ: Service ๋ ˆ์ด์–ด

์—ญํ• 

  • bounds ๊ธฐ๋ฐ˜ SQL ์ฟผ๋ฆฌ ์ƒ์„ฑ
  • ๋ทฐํฌํŠธ ๋‚ด ์ˆ™์†Œ๋งŒ ํ•„ํ„ฐ๋ง

ํ•ต์‹ฌ ์ฝ”๋“œ

// backend/src/modules/accommodation/accommodation.service.ts

async findAll(searchDto: SearchAccommodationDto) {
  const { swLat, swLng, neLat, neLng } = searchDto;

  const query = this.accommodationRepository
    .createQueryBuilder('accommodation')
    .leftJoinAndSelect('accommodation.amenities', 'amenity')
    .leftJoinAndSelect('accommodation.host', 'host')
    .leftJoinAndSelect('accommodation.reviews', 'review');

  // ์ง€๋„ ๊ธฐ๋ฐ˜ ๊ฒ€์ƒ‰
  // 1. bounds๊ฐ€ ์žˆ์œผ๋ฉด ๋ทฐํฌํŠธ ๊ธฐ๋ฐ˜ ์‚ฌ๊ฐํ˜• ๊ฒ€์ƒ‰
  // 2. bounds๊ฐ€ ์—†์œผ๋ฉด ์ค‘์‹ฌ์  + ๋ฐ˜๊ฒฝ ๊ธฐ๋ฐ˜ ๊ฒ€์ƒ‰ (ํ•˜์œ„ ํ˜ธํ™˜์„ฑ)
  const hasBounds =
    swLat !== undefined &&
    swLng !== undefined &&
    neLat !== undefined &&
    neLng !== undefined;

  if (hasBounds) {
    // ๋ทฐํฌํŠธ ๊ธฐ๋ฐ˜ ์‚ฌ๊ฐํ˜• ๊ฒ€์ƒ‰
    query
      .andWhere('accommodation.latitude BETWEEN :swLat AND :neLat', {
        swLat,
        neLat,
      })
      .andWhere('accommodation.longitude BETWEEN :swLng AND :neLng', {
        swLng,
        neLng,
      });
  } else {
    // ์ค‘์‹ฌ์  + ๋ฐ˜๊ฒฝ ๊ธฐ๋ฐ˜ ๊ฒ€์ƒ‰ (๊ธฐ๋ณธ๊ฐ’: ์„œ์šธ ์ค‘์‹ฌ 10km)
    // ... ๊ธฐ์กด ๋กœ์ง
  }

  // ... ๊ธฐํƒ€ ํ•„ํ„ฐ (๊ฐ€๊ฒฉ, ์ธ์›, ์˜ˆ์•ฝ ๋‚ ์งœ ๋“ฑ)

  const [items, total] = await query.getManyAndCount();
  return { items, total, page, limit, totalPages };
}

์ƒ์„ฑ๋˜๋Š” SQL ์ฟผ๋ฆฌ ์˜ˆ์‹œ

SELECT accommodation.*, amenity.*, host.*, review.*
FROM accommodations accommodation
LEFT JOIN accommodation_amenities ON ...
LEFT JOIN amenities amenity ON ...
LEFT JOIN users host ON ...
LEFT JOIN reviews review ON ...
WHERE accommodation.latitude BETWEEN 37.50 AND 37.60
  AND accommodation.longitude BETWEEN 126.95 AND 127.05
  -- ๊ธฐํƒ€ ํ•„ํ„ฐ ์กฐ๊ฑด๋“ค
ORDER BY ...
LIMIT 20 OFFSET 0;

๐Ÿ”„ ๋ฐ์ดํ„ฐ ํ๋ฆ„ ์˜ˆ์‹œ

์‹œ๋‚˜๋ฆฌ์˜ค: ์‚ฌ์šฉ์ž๊ฐ€ ์ง€๋„๋ฅผ ๊ฐ•๋‚จ์—ญ์—์„œ ํ™๋Œ€๋กœ ๋“œ๋ž˜๊ทธ

  1. ์‚ฌ์šฉ์ž ์•ก์…˜
  2. ์ง€๋„๋ฅผ ๊ฐ•๋‚จ์—ญ → ํ™๋Œ€๋กœ ๋“œ๋ž˜๊ทธ
  3. MapView: bounds ์ถ”์ถœ
  4. { swLat: 37.54, // ํ™๋Œ€ ๋‚จ์ชฝ swLng: 126.91, // ํ™๋Œ€ ์„œ์ชฝ neLat: 37.56, // ํ™๋Œ€ ๋ถ์ชฝ neLng: 126.93 // ํ™๋Œ€ ๋™์ชฝ }
  5. SearchResultPage: ๋””๋ฐ”์šด์‹ฑ
  6. ์‚ฌ์šฉ์ž๊ฐ€ ๋“œ๋ž˜๊ทธ๋ฅผ ๋ฉˆ์ถ˜ ํ›„ 500ms ๋Œ€๊ธฐ → API ํ˜ธ์ถœ ์ค€๋น„
  7. API ์š”์ฒญ
  8. GET /accommodations?swLat=37.54&swLng=126.91&neLat=37.56&neLng=126.93 &checkIn=2024-01-01&checkOut=2024-01-02 &minPrice=50000&maxPrice=200000
  9. ๋ฐฑ์—”๋“œ: SQL ์‹คํ–‰
  10. WHERE latitude BETWEEN 37.54 AND 37.56 AND longitude BETWEEN 126.91 AND 126.93 AND price_per_night BETWEEN 50000 AND 200000 -- ์˜ˆ์•ฝ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ์ฒดํฌ ๋“ฑ
  11. ์‘๋‹ต
  12. { "items": [ { "id": 123, "title": "ํ™๋Œ€ ๊ทผ์ฒ˜ ์•„๋Š‘ํ•œ ์ˆ™์†Œ", "latitude": 37.55, "longitude": 126.92, "price_per_night": 80000, ... }, // ... ํ™๋Œ€ ์ง€์—ญ ์ˆ™์†Œ๋“ค๋งŒ ], "total": 45, "page": 1, "limit": 20 }
  13. ํ™”๋ฉด ์—…๋ฐ์ดํŠธ
  14. - ์ขŒ์ธก ๋ฆฌ์ŠคํŠธ: ํ™๋Œ€ ์ง€์—ญ ์ˆ™์†Œ 45๊ฐœ ํ‘œ์‹œ - ์šฐ์ธก ์ง€๋„: ํ•ด๋‹น ์ˆ™์†Œ๋“ค์˜ ๋งˆ์ปค๋งŒ ํ‘œ์‹œ

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™”

1. ๋””๋ฐ”์šด์‹ฑ (Debouncing)

๋ฌธ์ œ: ์‚ฌ์šฉ์ž๊ฐ€ ์ง€๋„๋ฅผ ๋น ๋ฅด๊ฒŒ ๋“œ๋ž˜๊ทธํ•˜๋ฉด ์ดˆ๋‹น ์ˆ˜์‹ญ ๋ฒˆ์˜ API ํ˜ธ์ถœ ๋ฐœ์ƒ

ํ•ด๊ฒฐ์ฑ…: 500ms ๋””๋ฐ”์šด์‹ฑ ์ ์šฉ

// ๋งˆ์ง€๋ง‰ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ํ›„ 500ms ๋™์•ˆ ์ถ”๊ฐ€ ์ด๋ฒคํŠธ๊ฐ€ ์—†์œผ๋ฉด API ํ˜ธ์ถœ
debounceTimerRef.current = window.setTimeout(async () => {
  // API ํ˜ธ์ถœ
}, 500);

ํšจ๊ณผ:

  • API ํ˜ธ์ถœ ํšŸ์ˆ˜: ์ดˆ๋‹น 30ํšŒ → 2์ดˆ์— 1ํšŒ
  • ์„œ๋ฒ„ ๋ถ€ํ•˜ ๋Œ€ํญ ๊ฐ์†Œ
  • ๋„คํŠธ์›Œํฌ ํŠธ๋ž˜ํ”ฝ ์ ˆ๊ฐ

2. SQL ์ธ๋ฑ์Šค

ํ•„์ˆ˜ ์ธ๋ฑ์Šค:

-- ์œ„๋„/๊ฒฝ๋„์— ๊ณต๊ฐ„ ์ธ๋ฑ์Šค ์ƒ์„ฑ
CREATE INDEX idx_accommodation_location
ON accommodations(latitude, longitude);

-- ๋˜๋Š” ๋ณตํ•ฉ ์ธ๋ฑ์Šค
CREATE INDEX idx_accommodation_lat ON accommodations(latitude);
CREATE INDEX idx_accommodation_lng ON accommodations(longitude);

ํšจ๊ณผ:

  • ์ฟผ๋ฆฌ ์‹คํ–‰ ์‹œ๊ฐ„: 1000ms → 10ms (์˜ˆ์‹œ)
  • BETWEEN ์กฐ๊ฑด ๊ฒ€์ƒ‰ ์ตœ์ ํ™”

3. ํŽ˜์ด์ง€๋„ค์ด์…˜

๊ธฐ๋ณธ ์„ค์ •: ํ•œ ๋ฒˆ์— 20๊ฐœ์”ฉ๋งŒ ์กฐํšŒ

limit?: number = 20;

ํšจ๊ณผ:

  • ์‘๋‹ต ํฌ๊ธฐ ๊ฐ์†Œ
  • ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„ ํ–ฅ์ƒ

๐ŸŽจ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ (UX)

1. ๋กœ๋”ฉ ์ƒํƒœ ํ‘œ์‹œ

{isLoading && (
  <div className="flex flex-col gap-4">
    {Array.from({ length: 5 }).map((_, index) => (
      <AccommodationCardSkeleton key={index} />
    ))}
  </div>
)}

ํšจ๊ณผ:

  • ์‚ฌ์šฉ์ž๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋กœ๋”ฉ ์ค‘์ž„์„ ๋ช…ํ™•ํžˆ ์ธ์ง€
  • ์Šค์ผˆ๋ ˆํ†ค UI๋กœ ๋ ˆ์ด์•„์›ƒ ๋ณ€ํ™” ์ตœ์†Œํ™”

2. ์—๋Ÿฌ ์ฒ˜๋ฆฌ

catch (err) {
  console.error('์ง€๋„ ์ด๋™ ์ค‘ ์ˆ™์†Œ ๊ฒ€์ƒ‰ ์˜ค๋ฅ˜:', err);
  // ์—๋Ÿฌ ๋ฐœ์ƒ ์‹œ ๊ธฐ์กด ๋ชฉ๋ก ์œ ์ง€ (์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ํ–ฅ์ƒ)
}

ํšจ๊ณผ:

  • ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜ ์‹œ์—๋„ ๊ธฐ์กด ๋ฐ์ดํ„ฐ ์œ ์ง€
  • ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ์ค‘๋‹จ ๋ฐฉ์ง€

๐Ÿ” ๋Œ€์•ˆ: ์›ํ˜• ๋ฐ˜๊ฒฝ ๊ฒ€์ƒ‰ vs ์‚ฌ๊ฐํ˜• ๋ทฐํฌํŠธ ๊ฒ€์ƒ‰

์›ํ˜• ๋ฐ˜๊ฒฝ ๊ฒ€์ƒ‰ (Haversine)

// ์ค‘์‹ฌ์  + ๋ฐ˜๊ฒฝ์œผ๋กœ ๊ฒ€์ƒ‰
{
  lat: 37.5665,
  lng: 126.978,
  radius: 5  // 5km ๋ฐ˜๊ฒฝ
}

์žฅ์ :

  • ๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ˜ ์ •ํ™•ํ•œ ๊ฒ€์ƒ‰
  • ์ค‘์‹ฌ์ ์—์„œ ์ผ์ • ๊ฑฐ๋ฆฌ ์ด๋‚ด ๋ณด์žฅ

๋‹จ์ :

  • ํ™”๋ฉด์— ๋ณด์ด์ง€ ์•Š๋Š” ์ˆ™์†Œ๋„ ํฌํ•จ๋  ์ˆ˜ ์žˆ์Œ
  • ์ง€๋„ ๋ชจ์–‘๊ณผ ๋ถˆ์ผ์น˜ (์ง€๋„๋Š” ์‚ฌ๊ฐํ˜•, ๊ฒ€์ƒ‰์€ ์›ํ˜•)

์‚ฌ๊ฐํ˜• ๋ทฐํฌํŠธ ๊ฒ€์ƒ‰ (ํ˜„์žฌ ๊ตฌํ˜„) โœ…

// ํ˜„์žฌ ํ™”๋ฉด ์˜์—ญ์œผ๋กœ ๊ฒ€์ƒ‰
{
  swLat: 37.50,
  swLng: 126.95,
  neLat: 37.60,
  neLng: 127.05
}

์žฅ์ :

  • ์ •ํ™•์„ฑ: ํ™”๋ฉด์— ๋ณด์ด๋Š” ์˜์—ญ๊ณผ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์ •ํ™•ํžˆ ์ผ์น˜
  • ์„ฑ๋Šฅ: SQL BETWEEN ์ฟผ๋ฆฌ๋กœ ๋น ๋ฅธ ๊ฒ€์ƒ‰
  • ์ง๊ด€์„ฑ: ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๋Š” ๊ฒƒ = ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ

๋‹จ์ :

  • ๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ˜ ์ •๋ ฌ์€ ๋ณ„๋„ ๊ณ„์‚ฐ ํ•„์š”

๐Ÿ“Š ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค

1. ๊ธฐ๋ณธ ๋™์ž‘ ํ…Œ์ŠคํŠธ

1. ์ง€๋„๋ฅผ ์„œ์šธ ์‹œ์ฒญ์œผ๋กœ ์ด๋™
2. 500ms ํ›„ API ํ˜ธ์ถœ ํ™•์ธ
3. ์„œ์šธ ์‹œ์ฒญ ์ฃผ๋ณ€ ์ˆ™์†Œ๋งŒ ํ‘œ์‹œ๋˜๋Š”์ง€ ํ™•์ธ

2. ๋””๋ฐ”์šด์‹ฑ ํ…Œ์ŠคํŠธ

1. ์ง€๋„๋ฅผ ๋น ๋ฅด๊ฒŒ ์—ฌ๋Ÿฌ ๋ฒˆ ๋“œ๋ž˜๊ทธ
2. ๋งˆ์ง€๋ง‰ ๋“œ๋ž˜๊ทธ ์ข…๋ฃŒ ํ›„ 500ms์—๋งŒ API ํ˜ธ์ถœ๋˜๋Š”์ง€ ํ™•์ธ
3. ์ค‘๊ฐ„ ๋“œ๋ž˜๊ทธ๋“ค์€ API ํ˜ธ์ถœ ์•ˆ ๋˜๋Š”์ง€ ํ™•์ธ

3. ํ•„ํ„ฐ ์œ ์ง€ ํ…Œ์ŠคํŠธ

1. ๋‚ ์งœ, ๊ฐ€๊ฒฉ, ์ธ์› ํ•„ํ„ฐ ์„ค์ •
2. ์ง€๋„ ์ด๋™
3. ์„ค์ •ํ•œ ํ•„ํ„ฐ๊ฐ€ ์œ ์ง€๋˜๋ฉด์„œ ์ง€์—ญ๋งŒ ๋ณ€๊ฒฝ๋˜๋Š”์ง€ ํ™•์ธ

4. ์—๋Ÿฌ ์ฒ˜๋ฆฌ ํ…Œ์ŠคํŠธ

1. ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ๋Š๊ธฐ
2. ์ง€๋„ ์ด๋™
3. ๊ธฐ์กด ์ˆ™์†Œ ๋ชฉ๋ก์ด ์œ ์ง€๋˜๋Š”์ง€ ํ™•์ธ

๐Ÿš€ ํ–ฅํ›„ ๊ฐœ์„  ๋ฐฉ์•ˆ

1. ๋ฌดํ•œ ์Šคํฌ๋กค

// ํ˜„์žฌ: ํŽ˜์ด์ง€๋„ค์ด์…˜
// ๊ฐœ์„ : ์Šคํฌ๋กค ์‹œ ์ž๋™์œผ๋กœ ๋‹ค์Œ ํŽ˜์ด์ง€ ๋กœ๋“œ

2. ํด๋Ÿฌ์Šคํ„ฐ๋ง

// ์คŒ ์•„์›ƒ ์‹œ ๊ฐœ๋ณ„ ๋งˆ์ปค ๋Œ€์‹  ํด๋Ÿฌ์Šคํ„ฐ ํ‘œ์‹œ
// ์˜ˆ: "๊ฐ•๋‚จ๊ตฌ (25๊ฐœ)"

3. ์บ์‹ฑ

// ์ด๋ฏธ ์กฐํšŒํ•œ ์˜์—ญ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹œ
// ๊ฐ™์€ ์˜์—ญ์œผ๋กœ ๋Œ์•„๊ฐˆ ๋•Œ API ์žฌํ˜ธ์ถœ ๋ฐฉ์ง€

4. WebSocket ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ

// ์ƒˆ๋กœ์šด ์ˆ™์†Œ ๋“ฑ๋ก ์‹œ ์‹ค์‹œ๊ฐ„ ๋งˆ์ปค ์ถ”๊ฐ€
// ์˜ˆ์•ฝ ์™„๋ฃŒ ์‹œ ์‹ค์‹œ๊ฐ„ ๋งˆ์ปค ์ œ๊ฑฐ

๐Ÿ“š ์ฐธ๊ณ  ์ž๋ฃŒ

  • Kakao Maps JavaScript API ๋ฌธ์„œ
  • TypeORM QueryBuilder ๋ฌธ์„œ
  • React ๋””๋ฐ”์šด์‹ฑ ํŒจํ„ด
  • Airbnb Engineering Blog

๐Ÿ ๊ฒฐ๋ก 

์ง€๋„ ๋ทฐํฌํŠธ ๊ธฐ๋ฐ˜ ์‹ค์‹œ๊ฐ„ ๊ฒ€์ƒ‰์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŠน์ง•์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

โœ… ์ •ํ™•์„ฑ: ํ™”๋ฉด์— ๋ณด์ด๋Š” ์˜์—ญ = ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ
โœ… ์„ฑ๋Šฅ: SQL ์ธ๋ฑ์Šค + ๋””๋ฐ”์šด์‹ฑ์œผ๋กœ ์ตœ์ ํ™”
โœ… ์‚ฌ์šฉ์ž ๊ฒฝํ—˜: ์Šค์ผˆ๋ ˆํ†ค UI + ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋กœ ์•ˆ์ •์ ์ธ UX
โœ… ํ™•์žฅ์„ฑ: ์ถ”๊ฐ€ ํ•„ํ„ฐ์™€ ์‰ฝ๊ฒŒ ๊ฒฐํ•ฉ ๊ฐ€๋Šฅ

์ด ๊ตฌ์กฐ๋Š” ์ง€๋„ ๊ธฐ๋ฐ˜ ๊ฒ€์ƒ‰ ์„œ๋น„์Šค์˜ ํ‘œ์ค€ ํŒจํ„ด์ด๋ฉฐ, ๋Œ€๊ทœ๋ชจ ํŠธ๋ž˜ํ”ฝ์—์„œ๋„ ์•ˆ์ •์ ์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

'๐Ÿ’™ ํ”„๋ก ํŠธ์—”๋“œ(FE)' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

CSR / SSR / SSG / ISR ๋ Œ๋”๋ง ๋ฐฉ์‹ ๋น„๊ต  (1) 2026.01.17
Next.js 16 + Turbopack + pnpm ๋ชจ๋…ธ๋ ˆํฌ: Docker ๋นŒ๋“œ ์™„์ „ ์ •๋ณต  (0) 2026.01.17
์›น ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ œ๋Œ€๋กœ ํ™œ์šฉํ•˜๊ธฐ: Transition, Keyframes, rAF, WAAPI ๋น„๊ต์™€ ํ™œ์šฉ๋ฒ•  (0) 2025.09.24
Webpack Tree Shaking ๋™์ž‘ ์›๋ฆฌ  (0) 2025.09.17
Webpack + Babel + TypeScript ์„ค์ • ๊ฐ€์ด๋“œ  (0) 2025.09.16
'๐Ÿ’™ ํ”„๋ก ํŠธ์—”๋“œ(FE)' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • CSR / SSR / SSG / ISR ๋ Œ๋”๋ง ๋ฐฉ์‹ ๋น„๊ต
  • Next.js 16 + Turbopack + pnpm ๋ชจ๋…ธ๋ ˆํฌ: Docker ๋นŒ๋“œ ์™„์ „ ์ •๋ณต
  • ์›น ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ œ๋Œ€๋กœ ํ™œ์šฉํ•˜๊ธฐ: Transition, Keyframes, rAF, WAAPI ๋น„๊ต์™€ ํ™œ์šฉ๋ฒ•
  • Webpack Tree Shaking ๋™์ž‘ ์›๋ฆฌ
์—ฐ์žŽ(lotus leaf)
์—ฐ์žŽ(lotus leaf)
  • ์—ฐ์žŽ(lotus leaf)
    lotus' s develog ๐Ÿƒ
    ์—ฐ์žŽ(lotus leaf)
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (79)
      • โœ๏ธ ๊ฐœ๋ฐœํšŒ๊ณ ๋ก (5)
      • ๐Ÿงฎ ์•Œ๊ณ ๋ฆฌ์ฆ˜ (3)
      • ๐Ÿ’™ ํ”„๋ก ํŠธ์—”๋“œ(FE) (19)
        • HTML (0)
        • CSS (0)
        • Javascript (0)
        • React (0)
        • Next.js (0)
        • webpack & babel (0)
      • ๐Ÿ’ป ๋ฐฑ์—”๋“œ(BE) (2)
        • Nest.js (0)
        • Express.js (0)
        • MySQL (1)
      • โš™๏ธ ์ธํ”„๋ผ(Devops) (2)
      • ๐Ÿค– AI (1)
      • ๐ŸŒ WEB (8)
      • ๐Ÿ’ป CS (16)
        • ์ž๋ฃŒ๊ตฌ์กฐ (0)
        • ์ปดํ“จํ„ฐ ๋„คํŠธ์›Œํฌ (1)
        • ์šด์˜์ฒด์ œ (0)
        • ์ธ๊ณต์ง€๋Šฅ (8)
        • ์›น ๋ณด์•ˆ (1)
        • ํด๋ผ์šฐ๋“œ ์ปดํ“จํŒ… (6)
      • ๐Ÿ–‹๏ธ DevLog (1)
      • ๐Ÿฆพ ๋กœ๋ณดํ‹ฑ์Šค (4)
      • ๐Ÿ“— ๋„ค์ด๋ฒ„๋ถ€์ŠคํŠธ์บ ํ”„ ์›น ๋ชจ๋ฐ”์ผ (0)
      • ๐ŸŽฎ Unity(C#) (10)
      • ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด (4)
        • C (4)
        • C++ (0)
        • Java (0)
        • Python (0)
      • MSA (1)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ์ˆœ์—ด
    ์ฝ”๋”ํŒจ๋“œ
    next.js12
    ์ŠคํŒธ๋ฉ”์ผ๋ถ„๋ฅ˜๊ธฐ
    c++
    isaac automator
    ๊ธฐ์ดˆ์•Œ๊ณ ๋ฆฌ์ฆ˜
    AWS
    nav2
    ์•Œ๊ณ ๋ฆฌ์ฆ˜
    ros workspace
    Devops #๋Œ€๊ทœ๋ชจํŠธ๋ž˜ํ”ฝ์ฒ˜๋ฆฌ
    ์ŠคํŒธ๋ถ„๋ฅ˜๊ธฐ
    ์กฐํ•ฉ
    hard margin svm
    turtlebot
    ์ดํ™”์—ฌ์ž๋Œ€ํ•™๊ต #๋„์ „ํ•™๊ธฐ์ œ
    gaussian rbf svm
    C#
    ๋ฐฑ์ค€
    advaned detail
    ๋ฆฌ์•กํŠธ
    ํŒŒ์ผํŠธ๋ฆฌ
    client-streaming
    ros bridge
    auto-scaling
    soft margin svm
    C
    deploy-aws
    c์–ธ์–ด
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.6
์—ฐ์žŽ(lotus leaf)
์ง€๋„ ๋ทฐํฌํŠธ ๊ธฐ๋ฐ˜ ์‹ค์‹œ๊ฐ„ ์ˆ™์†Œ ๊ฒ€์ƒ‰ ๊ตฌํ˜„
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”