interface, type 차이점이 뭘까(Typescript)

비슷하지만 동일하지 않은 interface vs type

타입스크립트에서 타입을 선언하기 위해서는 interface 또는 type 키워드를 사용합니다.

각각의 선언 방식은 다음과 같으며 =의 유무만 다릅니다.

// interface
interface Team{
  name : string
}

// type
type Team = {
  name : string
}

그리고 타입의 확장 방법은 다음과 같습니다.

// interface
interface City extends Team {
  city : string
}

// type
type City = Team & {
  city : string
}

interface와 type의 가장 큰 차이점은 바로 선언적 확장(Declaration Merging) 기능인데요.

선언적 확장이란 이미 선언된 타입 선언에 필드를 추가하는 것입니다.

하나는 가능하고 하나는 가능하지 않은데 interface만 가능한 것이 특징입니다.

사용 방법은 다음과 같습니다.

interface Team {
  name : string
}

interface Team {
  manager : string
}

위와 같은 방법으로 이미 선언된 interface에 다시 필드를 선언할 수 있습니다.

하지만 type을 다음과 같은 방법으로 사용하면 에러가 발생합니다.

type Team = {
  name : string
}


// Error 발생 -> 'Duplicate identifier 'Team'
type Team = {
  manager : string
}

추가로 type은 원시형(number, string 등) 데이터를 다른 이름으로 지정해서 사용할 수 있지만 interface는 불가능합니다.

자세한 내용은 다음 코드와 같습니다.

//type으로 원시형 데이터의 이름을 지정
type NameDataType = string;

const printName = (name : NameDataType ) => {
  console.log(name);
}

//interface는 불가
interface NameType extends string {
}

차이점을 신경써도 되지 않을 상황이라면 취향에 따라 선택하면 되지만 기본적으로는 interface를 쓰면 큰 문제가 없다고 합니다.

참고 : typescript 공식 문서

경부선 제로데이 택배 방문기, 주차 정보, 신분증(고속버스 당일 택배)

부산(사상) -> 서울(경부) 당일 택배 이용기

엄마의 손맛을 공수받고 싶을 때 주로 떠오르는 것은 스티로폼 박스 + 우체국 택배입니다.

타지 살이를 하는 사람들의 경험은 비슷할텐데요.

음식물이 부패되기 쉬운 여름철이나 급한 물건이 있을 때는 당일에 받을 수 있는 방법이 있습니다.

바로 고속버스 당일 택배인데요.

일반 여객의 남는 화물칸을 이용해 물건을 출발지에서 도착지로 보내므로 예매 사이트에서 일반 여객의 스케줄을 확인하면 됩니다.

제가 이번에 받은 택배는 냉동 식품 1박스로 무게는 약 10kg, 택배 비용은 10,000원입니다.

와이프가 좋아하는 제육볶음과 국이 가득.

그리고 찾으러 갈 상황조차 여의치 않은 경우 퀵으로 연계도 되어 있는 것 같아요!

1. 택배 찾으러 가기

발신자에게 수하물 번호와 도착 위치, 도착 시간을 확인합니다.

저는 신세계 백화점에 주차하고 고속터미널(경부선)으로 무턱대고 찾아갔으나

네비에 ‘제로데이택배’를 검색하면 주차장까지 바로 안내해줍니다.

지하철을 타고 간다면 경부선 버스 도착지이자 승객들이 내리는 곳까지 찾아갑니다.

꽤나 걸어야 하는데요. 끝까지 걸어가면 바로 옆에 건물이 덩그러니 있습니다.

이렇게 생긴 건물이구요.

차량을 이용해 방문한다면 다음과 같아요.

입구쪽 도로는 항상 복잡하므로 버스정류장을 지나면 바로 제일 오른쪽 차선으로 붙어야 편하게 접근할 수 있어요.

주차비의 경우 최초 15분 무료이며 30분당 3000원이라고 하네요(22년 8월 기준).

접수 센터 내부에 주차비 할인을 적용할 수 있는 컴퓨터가 있고 셀프로 차량 등록 시 10분 추가 무료 이용이 가능했어요.

이렇게 생긴 컴퓨터에요.

2. 택배 수령하기

용건에 따라 ‘도착 접수’ 또는 ‘발송 접수’ 신청서를 작성해야 하구요.


여기는 발송을 위한 창구


여기는 수령을 위한 창구


이렇게 접수증을 제출하고 신분증을 확인하면 ‘관계자 외 출입금지’ 문으로 들어가서 물건을 받을 수 있는 수령증을 건네줍니다.

하지만 고속도로 교통 상황에 따라 도착이 늦어지는 경우 저처럼 사진 찍고 놀면서 기다려야 할 수도 있습니다….(오늘의 포스팅이 탄생하게 된 배경..)

저는 칼 같은 사람이라 도착시간에 딱 맞춰갔더니 조금 기다렸네요..

물건이 도착하면 안쪽에 잘 보관하고 있으니 너무 서두르거나 시간에 딱 맞춰서 갈 필요는 없을 것 같아요.

무사 수령 완료의 기쁨.

3. 간략 사항 정리

  • 준비물 : 수하물 번호, 수령인 신분증
  • 주차 이용료 : 초기 15분 무료(등록 시 10분 추가 무료), 30분당 3,000원(22년8월)
  • 수령 소요 시간 : 버스 도착 후 약 10분 이내
  • 위치 : 서울특별시 서초구 신반포로 194 제로데이택배 통합센터
  • 네비 검색 : 제로데이택배
  • 홈페이지 : zerodayexpress.com/

이전에 다른 터미널에서는 버스가 들어오는 시간에 맞춰 도착하는 버스로 찾아가서 기사 아저씨에게 제 물건 주세요~~ 했던 것 같은데 꽤 많이 편리해진 것 같습니다.

발전하는 세상!

뉴발란스 2002, ML2002RC 구입기(Kream 후기)

ML2002RC, NBP7CB123G, Gray

뉴발란스 992의 하위 호환(?)으로 불리는 2002 그레이 구입 기록.

얼마 전 크림과 무신사의 자존심 사건을 통해 크림에 대해 알게 되었고 이번에 처음으로 제품을 구매를 하는 계기가 되었다.

특별 주간이라고 나이키 온라인 사이트에서 평소 구하기 힘든 덩크 제품 등이 풀리길래 구경해보니 제품마다 특정 사이즈만 품절인 상태였다.

어찌어찌 찾다보니 같은 신발이라도 사이즈별로 금액이 천차만별이었고 크림에서 정가보다 비싸게 팔리는 사이즈가 거의 품절인 상태였다.

리셀러 선생님들이 먼저 구매한 것 같은데 나 같은 느림보는 크림에서 구매하는 게 맘 편하지 싶다.

판매 오픈 시간에 나는 세상 모르고 집에서 멸치 육수나 우리고 있었겠지..

크림 접속 계기는 나이키였지만 구매는 뉴발란스로 이루어지는 의식의 흐름.

구매 후기이기 때문에 사진과 크림의 서비스 경험에 대해 올리겠다.

사진은 분리수거 직전의 신발처럼 나왔지만 오해입니다..

개인적으로 너무 맘에 드는 디자인과 색깔.

아주 오래전부터 이 디자인의 신발을 봐왔지만 개인적으로는 질리지 않아 좋다.

신나서 봉인 해제하고 나서야 사진들을 찍었지만 애플 제품과 비슷하게 신발 박스가 비닐로 밀봉이 되어 있고 신발마다 검수 완료 택 확인서, 그리고 스티커가 들어있다.

놀란 점은 배송 속도인데 토요일 오후에 주문했으나 월요일에 도착하는 스피디함!

솔찍히 토요일 오후에 주문하면서 “지금 주문 시 월요일 도착” 이라고 써있길래 속으로 뻥치시네 라고 생각했다…

크림에서 배송도 함께 관리하고 있는지 모르겠지만 이러면 크림에서 안 살 이유가 없잖아?라는 느낌이 들 정도로 빠르고 구매 후기도 만족스러웠다(광고비 받은 거 아니지만 받고 싶어요..).

판매 가격에서 3천원 할인은 박스 상태 불량이 이유.

어차피 박스는 내일 분리 수거함에 있을 테니 그것도 만족!

남자지만 발이 마이크로 사이즈라 245.

발 볼이 넓은 편이라 사이즈 딱 맞춰서 신으면 신발이 조금 뚱뚱해짐..ㅠㅠ

뉴발란스 530보다 조금 더 발 볼이 좁은 느낌이 들어요.

신발끈을 흰색으로 교체하면 더 산뜻할 것 같아 준비중!

[BOOK] 클린코드(Clean Code)

사람답게 살기 위한 한 달 한 권(2022/06)

궁극적으로 코드는 요구사항을 상세히 표현하는 언어다.

필요에 따라 요구 사항에 더 가까운 언어를 만들 수 있고 정형 구조를 뽑아내는 도구를 만들 수도 있다.

그러나 정밀한 표현은 항상 필요하고 이 필요성은 없앨 방법이 없으므로 코드도 항상 존재할 수 밖에 없다.

코드 작성 시 시간을 들여 깨끗한 코드를 만드는 노력이 결국에는 비용 절감 뿐 아니라 전문가로서 살아남는 길이라고 한다.

일정에 쫓기더라도 대다수의 관리자는 좋은 코드를 원하며 좋은 코드를 사수하는 일은 바로 프로그래머의 책임이다. 

정해진 기한을 바꾸는 유일한 방법은 언제나 코드를 깨끗하게 유지하는 습관이고 깨끗한 코드는 보기에 즐거운 코드다.

깨끗한 코드는 보는 사람에게 즐거움을 선사해야 하고 세세한 사항까지 꼼꼼하게 처리하는 코드다.

깨끗한 코드는 한 가지를 잘한다.

나쁜 코드는 너무 많은 일을 하려고 애쓰다가 의도가 뒤섞이고 목적이 흐려지게 된다.

깨끗한 코드는 단순하고 직접적이며 잘 쓴 문장처럼 읽히고 설계자의 의도를 숨기지 않는다.

크기가 큰 코드보다는 작은 코드가 가치있으며 코드는 작을수록 좋다.

깨끗한 코드는 주의깊게 작성한 코드다.

시간을 들여 깔끔하고 단정하게 정리한 코드다.

세세한 사항까지 꼼꼼하게 신경쓴 코드다.

프로그램을 단순하게 보이도록 만드는 열쇠는 언어가 아니라 프로그래머다.

변수, 함수, 클래스 네이밍 시 의미있는 이름을 사용해야 한다.

의도를 분명히 해야 하고 좋은 이름을 지으려면 시간이 걸리지만 최종적으로는 이 이름을 통해 절약하는 시간이 더 많다.

변수, 함수, 클래스 이름은 존재 이유와 수행 기능, 사용 방법을 나타내도록 하여 주석이 필요가 없도록 한다.

당연하게도 그릇되거나 오해를 불러일으킬 수 있는 이름은 피한다.

예를 들면 실제는 List가 아니지만 itemList를 사용하거나 O(대문자 o), l(소문자 L)를 사용하여 헷갈리게 만들거나 prevGetNameAndAgeData, prevGetNameAndSexData 등과 같이 한 눈에 구분하기 어려운 이름이다.

항상 의미를 갖고 구분을 해야 하며 컴파일러나 인터프리터만 통과하려는 의도로 코드를 구현하면 안된다.

연속된 숫자를 덧붙이거나 의도를 드러내지 않는 이름은 작성하지 말고 읽는 사람이 차이를 알도록 지어야 좋은 이름이다.

의미 이외에도 사용자가 발음하거나 검색하기 쉬운 이름을 사용한다.

예를 들면 GenYrDt보다 GenerateYearDate가 훨씬 낫다.

이름 길이는 범위 크기에 비례해야 한다.

왜냐하면 나중에 코드가 복잡해졌을 때 ab와 같은 단순한 이름은 검색이 거의 불가능하기 때문이다.

항상 남들이 이해하기 쉽게 코드를 작성한다.

전문가라고 불리는 프로그래머는 명료함을 최우선으로 하여 항상 남들이 이해하는 코드를 작성한다.

클래스와 객체 명명 시에는 명사, 명사구가 적합하며 동사는 사용하지 않는 것이 좋다.

그러나 메서드는 동사나 동사구가 적합하다.

명명 시 기발하거나 보편적으로 이해하기 어려운 이름은 피하는 게 좋으며 예를 들어 특정 문화를 이해해야만 알 수 있는 단어나 구어체, 속어 등이 있다.

한 개념에서는 하나의 이름만 선택하고 get, retrieve, bring, fetch, take 등 같은 기능의 메서드는 이름을 통일한다. 

이름이 다르면 클래스와 타입도 다르게 여기기 쉬우므로 일관성있는 어휘를 사용하고 일관성 속에서 맥락을 알 수 있도록 해야 한다.

예를 들어 두 값을 더하는 메서드가 add라면 하나의 값 만을 더하는 메서드는 insert, append로 짓는 것이다.

해법 영역은 기술 개념에서 가져온 이름이 이해하기 쉬운데 알고리즘, 수학 용어, 패턴 이름 등 이미 익숙한 개념이 이해하기 쉽다.

하지만 문제 영역은 해당 영역에서 가져온 이름도 문제가 없다.

의미 있는 맥락context을 사용하여 클래스, 함수에 맥락을 부여하거나 접두어를 사용해 의미를 분명하게 한다.

또한 불필요한 맥락context은 사용하지 말고 의미가 분명한 경우에 한해 짧은 이름이 긴 이름보다 좋으므로 불필요한 부분을 추가하지 않도록 한다.

문장이나 문단처럼 읽히는 코드가 아니면 표나 자료구조처럼 읽히는 코드를 짜는 데 집중하는 것이 좋다.

함수는 무조건 작게 만드는 것이 좋고 중첩 구조가 생길만큼 함수가 커지는 것은 좋지 않다.

함수는 한 가지만을 해야 한다.

중복은 모든 소프트웨어에서 악의 근원이다.

하위 루틴을 발명한 이래로 소프트웨어 개발에서 지금까지의 혁신은 소스 코드의 중복을 제거하려는 지속적인 노력으로 볼 수 있다.

소프트웨어를 짜는 것은 글짓기와 비슷하다.

처음에는 길고 복잡하고 정리가 안된 상태지만 코드를 다듬고 정리하여 최종적으로 사용할 수 있는 코드를 만드는 것이다.

프로그래밍의 기술은 언제나 언어 설계의 기술이다.

프로그래밍의 대가(大家)는 시스템을 구현할 프로그램이 아니라 풀어갈 이야기로 여긴다. 

주석은 언제나 실패를 의미하며 코드로 의도를 표현하지 못한 실패를 만회하기 위해 사용하는 것으로 본다.

따라서 주석이 필요한 상황에 처하면 코드로 의도를 표현할 방법을 먼저 찾는다.

주석은 오래될수록 코드에서 멀어지며 내용이 잘못될 가능성도 커진다.

코드를 유지보수해도 주석까지 유지보수하기는 힘들기 때문이다.

진실은 오직 한 곳, 코드에만 존재한다.

처음부터 바르게 시스템을 만들 수 없다.

대신 오늘 주어진 사용자 스토리에 맞춰 시스템을 구현하고 내일은 새로운 스토리에 맞춰 시스템을 조정하고 확장한다.

이것이 반복적이고 점진적인 애자일(Agile) 방식의 핵심이다.

켄트 벡(Kent Beck)이 제기하는 단순한 설계 규칙 네 가지는 순서대로 다음과 같다.

  • 모든 테스트를 실행한다.
  • 중복을 없앤다.
  • 프로그래머의 의도를 표현한다.
  • 클래스와 메서드 수를 최소화한다.

테스트 가능한 시스템을 만들려고 애쓰면 설계 품질이 더불어 높아지므로 테스트는 클린 코드를 위한 필수 요소이다.

테스트 케이스가 많을수록 개발자는 테스트가 쉽도록 코드를 작성하게 되고 철저히 테스트 가능한 시스템을 만들면 더 나은 설계를 얻을 수 있다.

결합도가 높으면 테스트 케이스 작성이 어려우므로 결합도를 낮추면 설계 품질은 더욱 높아진다. 

Emotion(Styled), Props 전달하기(Typescript)

Typescript와 함께 동적 CSS 스타일 사용하기
import styled from "@emotion/styled";

const StyledPropsTest = styled.div`
  width: 120px;
  height: 40px;
  font-size: 25px;
  background: ${(props) => (props.testProps ? "blue" : "red")};
  color: white;
`;

const StyledTest = ({testprops}) => {
  return(
    <StyledPropsTest testProps={testprops}>Prop Test</StyledPropsTest>
  )
}

export default StyledTest;

컴포넌트 내부에서 사용하는 변수를 CSS에 전달하여 동적 스타일을 적용하기 위해서는 위와 같이 props를 전달하는 방법이 있습니다.

하지만 Typescipt를 사용하여 custom props를 사용하면 다음과 같은 에러를 만나게 됩니다.

(property) testProps: boolean
Type ‘{ children: string; testProps: boolean; }’ is not assignable to type ‘IntrinsicAttributes & { theme?: Theme | undefined; as?: ElementType<any> | undefined; } & ClassAttributes<HTMLDivElement> & HTMLAttributes<…>’.
Property ‘testProps’ does not exist on type ‘IntrinsicAttributes & { theme?: Theme | undefined; as?: ElementType<any> | undefined; } & ClassAttributes<HTMLDivElement> & HTMLAttributes<…>’.ts(2322)

이는 타입스크립트에서 props의 타입을 체크하기 때문인데요.

다음과 같이 간단하게 <{data:type}>의 타입 단언(Type Assertion)으로 해결할 수 있습니다.

const StyledPropsTest = styled.div<{testProps:boolean}>`
  width: 120px;
  height: 40px;
  font-size: 25px;
  background: ${(props) => (props.testProps ? "blue" : "red")};
  color: white;
`;

다음과 같이 에러가 사라진 것을 확인할 수 있습니다.

한우리 본점에서 국수전골 마시기(토요일 런치)

격식있는 자리 또는 외국인 친구에게 소개하기 좋은 맛

롯데타워점에서 맛본 뒤로 꾸준히 찾게 된 한우리의 국수전골!

최근 와이프의 최애 음식입니다.

둘이 먹다가 하나가 어쩌고 하는 극단적인 예를 들지 않더라도 충분히 미식의 즐거움을 느낄 수 있는 맛입니다.

본점은 어떨까 싶어 토요일에 방문을 해보았습니다.

양은 조금 차이가 난다고 하지만 실제로 느끼기에는 미미할 정도의 차이로 런치 가격에 먹을 수 있다니!

앞으로 본점의 단골이 되기로 했습니다.

한우국수전골의 가격은 33,000원이지만 런치는 1인당 25,000원으로(물론 이것도 싼 금액은 아니지만..) 매우 만족스런 한 끼였습니다.

죽 추가는 1인분에 5,000원이었던 것 같아요.

주차 시 발렛은 필수인 것 같구요.

발렛비는 3,000원이었습니다.

2층의 분위기는 이렇지만 본점은 건물을 통째로 사용하고 있다보니 다른 층들도 한번씩 방문해보고 싶었습니다.

아래처럼 후문 출구 쪽에는 반찬이나 요리도 포장 구입이 가능하더라구요.

Homepage : http://www.hwrfood.com/

Address : 서울시 강남구 도산대로304 (신사역 1번, 강남구청 3번, 압구정로데오역 5번 출구)

leaflet, 반복되는 지도에 marker 표시하기(react)

맵 축소 시 반복되는 지도에 Marker 반영하기

다른 옵션을 설정하지 않는 한 맵을 최대로 축소하면 다음과 같이 지도가 반복되어 나타납니다.

여기서 맵을 오른쪽이나 왼쪽으로 넘겨서 중심을 바꿔도 Marker는 그대로 있습니다.

하지만 맵 렌더링 시 다음과 같이 ‘worldCopyJump’ 옵션을 넣어주면 중심이 이동하더라도 기존의 Marker를 모두 표시할 수 있습니다.

<MapContainer
   center={[35.102, 129.067]}
   zoom={5}
   scrollWheelZoom={true}
   worldCopyJump
>

옵션 하나만으로 맵을 이동하면 Marker도 이동하여 같은 좌표에 표시되는 것을 확인할 수 있습니다.

leaflet 맵 표시 언어 변경

지도 데이터 서버 변경하기

leaflet의 기본 맵은 각 지역마다 현지 나라의 언어로 표기되어 한국 맵은 한국어, 일본 맵은 일본어, 프랑스 맵은 프랑스어로 표기되어 있습니다.

하지만 전체 맵을 영어로 표기하고 싶거나 산맥, 해양 등 용도에 맞게 지도의 이미지를 변경하고 싶을 때는 아래 링크에서 원하는 맵의 유형과 서버를 확인하여 서버만 변경해주면 됩니다.

1. 링크에서 서버 확인

http://leaflet-extras.github.io/leaflet-providers/preview/

링크에 접속하면 다음과 같은 화면입니다.

초록색 네모칸에서 서버 정보, 빨간색 네모칸에서 맵의 종류 샘플을 볼 수 있습니다.

샘플로 OpenStreetMap.France를 적용해보도록 하겠습니다.

빨간 네모칸에서 OpenStreetMap.France을 선택하면 초록 네모칸에 서버 정보가 표시됩니다.

‘https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png’

‘&copy; OpenStreetMap France | &copy; <a href=”https://www.openstreetmap.org/copyright”>OpenStreetMap</a> contributors’

이 두 부분만 코드에서 변경해주면 바로 적용이 됩니다.

2. 코드 적용

import { MapContainer, TileLayer, useMap, Marker, Popup } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import { icon } from "leaflet";
const Icon = icon({
  iconUrl: "marker-icon.png",
  iconSize: [16, 16],
  iconAnchor: [12, 16],
});

const MyMap = () => {
  return (
    <MapContainer
      center={[37.56675, 126.97842]}
      zoom={10}
      scrollWheelZoom={true}
      style={{ width: "500px", height: "500px" }}
    >
      <TileLayer
        attribution='&copy; OpenStreetMap France | &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png"
      />
      <Marker position={[37.56675, 126.97842]} icon={Icon}>
        <Popup>서울시청이에요.</Popup>
      </Marker>
    </MapContainer>
  );
};

export default MyMap;

설정에 따라 변경된 맵과 언어를 확인할 수 있습니다.

CSS, calc()를 사용해 main(body) 높이 자동 설정하기

CSS에서 calc 메서드 사용하기

CSS에서 calc() 메서드를 사용하면 window 창의 사이즈 변경에 따라 자동으로 길이를 계산할 수 있습니다.

header, main, footer의 레이아웃을 나눌 때 main에서 calc()를 사용하게 되면 윈도우 창의 크기가 줄어들어도 main의 내용이 잘릴 염려 없이 가변적으로 창의 크기에 맞게 표현할 수 있습니다.

1. 필요성

예를 들면 다음과 같은 상황에서 유용합니다.

위와 같은 화면이 있을 때 윈도우 창의 사이즈를 줄이면(resizing) 가운데 main의 스크롤을 끝까지 내려도 데이터가 모두 표시되지 않을 때가 있습니다.

스크롤을 끝까지 내렸지만 이렇게 r 까지만 표시되고 아랫부분은 짤려서 표시되지 않습니다.

이런 상황에서 main의 div 높이를 계산하여 자동으로 조절해주면 다음과 같이 윈도우가 리사이징 되더라도 화면이 짤리지 않고 유지되는 것을 볼 수 있습니다.

2. 적용하기

main div의 높이(height)에 calc() 메서드를 사용하여 ‘뷰 높이(vh) – 나머지 공간’을 해주면 됩니다.

다만 전달값의 +, – 앞 뒤에는 반드시 공백을 삽입해야 합니다.

// OK
height: calc(100vh - 300px);

// 에러(공백 없음)
height: calc(100vh-300px);

결과는 다음과 같습니다.

Next.js + Leaflet(OSM) Marker 표시하기

Next.js에서 Leaflet Marker 이미지 로딩하기

React-leaflet에서 제공하는 기본 설정 방법에 따라 leaflet 코드를 구현하여도 맵은 표시되지만 Marker 이미지는 깨져서 표시됩니다.

Next.js에서 이미지는 Next/image와 이미지 상대 주소를 import하여 사용하면 잘 로딩이 되지만 상대 경로 url을 직접 사용하면 작동하지 않습니다.

예를 들면 다음과 같은 상황입니다.

import Image from "next/image";
import logo from "../../styles/images/logo.png";

//작동함
<Image src={logo} alt="logo />

//작동하지 않음
<Image src={"../../styles/images/logo.png"} alt="logo" width="100px" height="100px" />

이는 Next.js에 정해진 폴더 규칙이 있기 때문인데요. 만약 경로를 사용해 이미지나 파일을 가져오고 싶다면 public 폴더를 이용해야 합니다.

빌드 후 기본 폴더는 public이므로 public 폴더 내 logo.png 파일을 넣는 경우 /logo.png로 접근이 가능합니다.

import Image from "next/image";

<Image src={"/logo.png"} alt="logo" width={"100px"} height={"100px"} />

Marker 이미지 표시하기

이를 참고하면 Marker 이미지 부분도 icon을 사용해서 응용할 수 있습니다.

public 폴더 내 images 폴더를 만들고 logo.png 파일을 넣은 뒤 다음과 같이 사용합니다.

import { MapContainer, TileLayer, useMap, Marker, Popup } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import { icon } from "leaflet";

const Icon = icon({
  iconUrl: "images/logo.png",
  iconSize: [24, 24],
  iconAnchor: [12, 24],
});

const MyMap = () => {
  return (
    <MapContainer
      center={[37.56675, 126.97842]}
      zoom={10}
      scrollWheelZoom={true}
      style={{ width: "500px", height: "500px" }}
    >
      <TileLayer
        attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      <Marker position={[37.56675, 126.97842]} icon={Icon} >
        <Popup>서울 시청</Popup>
      </Marker>
    </MapContainer>
  );
};

export default MyMap;