728x90

개인적으로 사용할 목적으로 크롬 확장자를 만드는 김에 갑자기 다국어처리도 하고 싶어졌다.

 

만들어놓고 갑자기 확장프로그램 스토어에 업로드 할 수도 있으니깐...??

 

일단 크롬 확장 프로그램 (chrome extension)은 크롬에서만 모바일도 아닌 웹에서만 사용 가능한 툴이다.

 

확장프로그램 개발가이드는 나중에 다시 상세하게 작성해보고... 이번에는 다국어처리만 진행해보겠습니다.

 

먼저 공식 가이드 링크는 아래와 같습니다.

 

https://developer.chrome.com/docs/extensions/reference/api/i18n?hl=ko#getmessage

 

chrome.i18n  |  API  |  Chrome for Developers

chrome.i18n 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 설명 chrome.i18n 인프라를 사용하여 전체 앱 또는 확장 프로그램에 국제화를 구현합니다. 매니페스트

developer.chrome.com

 

방식은 _locales > ko,en 등 설정할 언어디렉토리를 만들고 각 디랙토리 안에 messages.json파일을 만들어서 메뉴나 메세지 등 다국어 처리를 진행 할 문장들을 각 언어에 맞게 작성해주는 것이 첫번째 입니다.

다국어 처리를 위한 디렉토리 구조

저는 크롬 확장프로그램을 개발할 때 누군가가 개발을 편하게 할 수 있도록 만든 cli를 사용했습니다. 해당 cli도 나중에 작성해보겠습니다

 

폴더 구조는 위의 사진처럼 manifest.json 이 위치해 있는 디렉토리에 _locales 디렉토리가 위치하면 됩니다.

 

이 떄 locales 가 아니라 _locales 입니다. _ 는 오타가 아닙니다!

 

그 다음 각 언어별 디렉토리 안에 messages.json 파일을 생성합니다.

 

지원가능 언어 및 언어별 디렉토리명은 아래의 링크에서 확인이 가능합니다.

 

https://developer.chrome.com/docs/extensions/reference/api/i18n?hl=ko#locales

 

chrome.i18n  |  API  |  Chrome for Developers

chrome.i18n 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 설명 chrome.i18n 인프라를 사용하여 전체 앱 또는 확장 프로그램에 국제화를 구현합니다. 매니페스트

developer.chrome.com

 

그리고 messages.json 안에는 아래와 같이 작성해줍니다. 

"입력할_코드": {
	"message":"출력될 문자"
}

 

위의 규칙을 반드시 지키면서 작성해야 합니다.

 

다음으로 확장프로그램 기본 정보에서 다국어 처리를 하는 경우는 manifest.json 에서 아래의 이미지와 같이 작성해줍니다.

 

확장프로그램 이름에 직접 문자열을 입력하는게 아닌 messages.json에 입력한 값을 가져오기 위해 __MSG_appName__  을 입력,

 

description 에도 __MSG_welcomeMessage__ 로 작성하고 배포하면 확장프로그램 목록에서 확장프로그램명과 간단한 설명이 정상적으로 출력되는 모습을 볼 수 있습니다.

 

다음으로는 확장프로그램의 팝업과 서브페이지 등에서 다국어를 처리하는 방법입니다.

 

팝업이나 서브페이지 같은 경우는 html과 js,css 와 같이 일반적은 웹 페이지 형식으로 구성되어 있다보니 manifest.json에 적용한 방식과

 

다르게 작성해야합니다.

 

바로 간단한 스크립트를 통해 다국어 처리를 해주어야 합니다.

 

약간의 반 노가다(?) 식으로 다국어처리를 통해 문구를 보여줄 요소들을 다 찝어와서 messages.json에 미리 등록해준 텍스트를 넣어주면 끝!

 

일단은 개인적으로 사용하는게 1차 목적이라 반 노가다 형식으로만 만들어봤는데...

 

나중에 상황에 따라 좀 더 나은 방법을 찾아보고 추가 작성하겠습니다...

 

잘못된 부분 지적은 언제나 감사합니다.

728x90
728x90

작업 중 발견 된 현상인데...

특정 아이폰 사파리 브라우저에서만 text-overflow:ellipsis 가 적용되지 않는 문제가 발생했다...

맥에다 아이폰을 연결해서 디버깅해봐도... 아니 대체 왜!!! 왜 특정 아이폰만 그러는지!!!

 

구글링 해본 결과 display 속성부터 position 속성까지 변경하는 방법들이 다양했는데

 

일단 내가 사용한 방법은 overflow:hidden 이 아닌 overflow:auto를 적용하는 것 이었다.

 

특이하게도 딱 overflow:auto로 값을 변경하니 text-overflow:ellipsis가 적용되었는데.... 아직도 원인은 모르겠다...

 

다만 작업중인 부분이 모바일 전용이고 해당 부분 위에 가상선택자로 투명한 박스를 올려놔서 사용자가 스와이프해도

 

해당 글 부분의 스크롤이 동작하지는 않는다.

 

.text_box {width:95%; white-space:nowrap; overflow:auto; text-overflow:ellipsis; scrollbar-width:none;}
.text_box::-webkit-scrollbar {display:none; -ms-overflow-style:none; scrollbar-width:none}

 

나중을 위해 기록해두기!

728x90
728x90
npx react-native init 프로젝트명

내가 안까먹기위한 기록!

 

2024-11-05 expo 가 아닌 네이티브 방식으로 생성하기 변경

1. 기존에 설치된  react-native-cli @react-native-community/cli 제거

npm uninstall -g react-native-cli @react-native-community/cli

 

 2. 새 프로젝트 만들기

npx @react-native-community/cli@latest init AwesomeProject
728x90
728x90

sql문에서 사용되는 컬럼의 옵션들을 nest에서는 어떻게 설정하고 사용되는지 알아보기!

 

1. @Column에 간단한게 작성하는 법

// ProjectRootDir/src/TableNameDir/entities/TableName.entity.ts

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class colunmName {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column({ nullable: true })
  description: string;
}

entity.ts에 데코레이터를 작성하는 간단한 예시입니다.

만약 아무 데코레이터를 설정하지 않으면 해당 컬럼은 name varchar NOT NULL, 로 생성됩니다.

 

2. 데코레이터 종류

이름 설명
nullable 해당 컬럼의 null 허용 여부 boolean
type 해당 컬럼의 타입을 지정 string
length 해당 컬럼의 문자열 길이를 지정 (최대 255) number
unique 해당 컬럼 값이 유일해야함을 지정 boolean
default 해당 컬럼의 기본값을 지정 any
comment 해당 컬럼값에 설명을 추가 string
precision 해당 컬럼값이 number인 경우 정밀도를 설정 number
scale 해당 컬럼값이 number인 경우 소수점 자릿수를 지정 number
array 해당 컬럼값이 배열인지를 결정 string
enum 해당 컬럼값이 지정된 enum 값 중 하나여야 함을 나타냄 [string]
primary 해당 컬럼이 기본 키값임을 명시, 일반적으로 @PrimaryGeneratedColumn() 를 통해 지정  

 

앞으로 자주 사용 될 것 같으니 내가 알아볼 수 있게 기록해두기!

728x90

'Programming > Nestjs' 카테고리의 다른 글

[Nestjs] Nestjs 시작하기  (1) 2024.01.28
728x90

일전에 사이드프로젝트를 진행하면서 로컬스토리지와 세션스토리지 그리고 쿠키를 이용했었고

지금도 회사업무를 진행하면서 세션과 로컬스토리지, 쿠키를 이용하고있습니다.

 

하지만.... 사이드프로젝트를 진행할 당시 뭔가... 뭔가가 부족했었는데...

요즘이야 백, 프론트 상관없이 올포지션을 다 다룰수 있어야하지만... 난 아직도 부족하기 때문에

백엔드 프로젝트를 혼자서 구현하기가 아직도 버거워요. 그러다가 문득 찾은 것이 indexedDB!!!

 

이름 그대로 DB이다! mdn web docs 에서는 이렇게 설명하고 있습니다.

 

IndexedDB는 파일이나 블롭 등 많은 양의 구조화된 데이터를 클라이언트에 저장하기 위한 로우 레벨 API입니다. IndexedDB API는 인덱스를 사용해 데이터를 고성능으로 탐색할 수 있습니다.

 

라고 설명하고 있습니다! 조금 더 자세한 설명은 아래의 링크로 접속하시면 볼 수 있습니다!

 

https://developer.mozilla.org/ko/docs/Web/API/IndexedDB_API

 

IndexedDB API - Web API | MDN

IndexedDB는 파일이나 블롭 등 많은 양의 구조화된 데이터를 클라이언트에 저장하기 위한 로우 레벨 API입니다. IndexedDB API는 인덱스를 사용해 데이터를 고성능으로 탐색할 수 있습니다. Web Storage는

developer.mozilla.org

 

1. React에 indexedDB 살짝 끼얹어보기

이제 진짜로 indexedDB를 React 프로젝트에 적용시켜보겠습니다.

일단 프로젝트의 root 파일에 indexedDB를 열어준다고(?) 작성을 해줍니다.

 

MDN 문서에서는 아래와 같이 나와있습니다.

// In the following line, you should include the prefixes of implementations you want to test.
window.indexedDB =
  window.indexedDB ||
  window.mozIndexedDB ||
  window.webkitIndexedDB ||
  window.msIndexedDB;
// DON'T use "var indexedDB = ..." if you're not in a function.
// Moreover, you may need references to some window.IDB* objects:
window.IDBTransaction =
  window.IDBTransaction ||
  window.webkitIDBTransaction ||
  window.msIDBTransaction;
window.IDBKeyRange =
  window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
// (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)

다만 TypeScript를 사용하면 window.indexedDB 만 인식되고 있었습니다.

 

저는 작성할 때 root 파일에 indexedDB를 초기화 하고 필요한 객체 저장소를 생성하는 함수를 실행시키게 설정해놨습니다.

 

export const indexedDBStart = () => {
  const indexedDB = window.indexedDB;
  const dbName = "tableName";
  const request = indexedDB.open(dbName, 1);
  request.onupgradeneeded = (e: any) => {
    const db = e.target.result;
    const articleStore = db.createObjectStore("article", {
      keyPath: "id",
      autoIncrement: true,
    });
    const memberStore = db.createObjectStore("member", {
      keyPath: "id",
      autoIncrement: true,
    });
  };
  request.onsuccess = (e: any) => {
  	console.log("성공");
  };
  request.onerror = (e: any) => {
    console.error("실패 : ", e.target.error);
  };
};

 

indexedDBStart 가 실행되면 dbName 이라는 이름의 DB, 그리고 버전은 1인 indexedDB 를 열어달라고 요청합니다. 그리고 만약 해당 indexedDB가 없으면 dbName, 버전 1 이라는 indexedDB를 생성하게 됩니다.

 

request.onupgradeneeded 를 통해 데이터베이스 객체를 가져온 후 article 과 member 란 객체 저장소를 생성하게 됩니다.

 

그리고 객체 저장소 생성에 성공하면 console.log에 '성공' 이라는 메시지가 출력되고

만약 실패한다면 '실패 : error' 가 출력됩니다.

 

자 이제 개발자모드를 켜서 확인해보시면 아래와 같이 브라우저 저장소가 생성되어있습니다!

2. indexedDB에 데이터 저장해보기

이제 저장소를 만들었으니 해당 저장소에 post 하는 방법을 알아보겠습니다.

먼저 article 에 게시글을 저장해보겠습니다.

 

export const indexedDBPost = (postDatas: any) => {
  const idb = window.indexedDB;
  return new Promise((resolve, reject) => {
    const dbOpen = idb.open("tableName", 1);
    dbOpen.onsuccess = () => {
      const db = dbOpen.result;
      const transaction = db.transaction("article", "readwrite");
      const tableNameDB = transaction.objectStore("article");

      const postData = tableNameDB.put({
        title: postDatas.title,
        contents: postDatas.contents,
        createDt: postDatas.createDt,
        writer: postDatas.writer,
      });

      postData.onsuccess = (e) => {
        transaction.oncomplete = () => {
          db.close();
        };
        resolve(e.type);
      };

      postData.onerror = (e) => {
        console.error("error : ", e);
        reject(e);
      };

      transaction.oncomplete = () => {
        db.close();
      };
    };
  });
};

해당 함수를 통해 글 작성 버튼을 누르면 아래의 캡처화면과 같이 indexedDB에 게시글 정보가 저장됩니다.

 

해당 캡쳐에서는 샘플용으로 간단하게 작성해서 데이터를 작성했지만 실제로는 쿠키나  스토리지보다 더 많은 데이터를 저장시킬 수 있습니다.

 

해당 캡쳐는 제가 사이드프로젝트를 하면서 indexedDB를 활용하여 작성 한 데이터 샘플입니다. 캡쳐본처럼 다양한 데이터를 저장시킬 수 있는 장점이 있습니다.

 

3. indexedDB에 저장된 데이터 불러오기

그럼 간단하게 데이터를 저장해봤는데 이번에는 저장된 데이터를 불러오는 방법을 알아보겠습니다!

 

export const getAllDataFromIndexedDB = () => {
  const idb = window.indexedDB;
  return new Promise((resolve, reject) => {
    const dbOpen = idb.open("tableName", 1);

    dbOpen.onsuccess = () => {
      let db = dbOpen.result;
      const transaction = db.transaction("article", "readonly");
      const reviewDB = transaction.objectStore("article");
      const review = reviewDB.getAll();

      review.onsuccess = (e: any) => {
        const data = e.srcElement.result;
        console.log('성공 데이터 불러오기',data);
        resolve(data);
      };

      review.onerror = (e) => {
        console.log("error", e);
        reject(e);
      };

      transaction.oncomplete = () => {
        db.close();
      };
    };
  });
};

 

indexedDB에 저장된 데이터를 불러오는 코드입니다. 기본적은 구조는 post와 비슷합니다.

먼저 오픈 할 테이블을 선택해준 후 dbOpen 이 성공하면 해당 db안의 컬럼을 선택해주고 컬럼안의 데이터들을 불러와주면 끝입니다.

 

정상적으로 console에 찍히는 모습을 볼 수 있습니다.

 

만약 저장된 데이터가 여러개라면 아래의 캡쳐와 같이 console에 찍히게 됩니다!

 

이제 컴포넌트에서 불러온 데이터를 state애 저장 후 사용하면 됩니다!

 

만약 특정한 값의 데이터만 불러오고 싶은 경우가 있는데 저는 filter를 통해 특정 데이터를 선택한 후 resolve에 넣어주어 사용하고 있습니다.

 

// 특정 데이터 선별해서 사용하기

export const getAllDataFromIndexedDB = (postWriter:string) => {
  const idb = window.indexedDB;
  return new Promise((resolve, reject) => {
    const dbOpen = idb.open("tableName", 1);

    dbOpen.onsuccess = () => {
      let db = dbOpen.result;
      const transaction = db.transaction("article", "readonly");
      const reviewDB = transaction.objectStore("article");
      const review = reviewDB.getAll();

      review.onsuccess = (e: any) => {
        const data = e.srcElement.result;
        const filterData  = data.filter((item) => item.writer === postWriter)
        console.log('필터 불러오기',filterData);
        resolve(filterData);
      };

      review.onerror = (e) => {
        console.log("error", e);
        reject(e);
      };

      transaction.oncomplete = () => {
        db.close();
      };
    };
  });
};

 

 

4. indexedDB에 저장된 데이터 삭제하기

삭제또한 매우 간단합니다. indexedDB에 저장 시 자동으로 생성되는 id값으로 데이터 삭제를 진행합니다.

 

export const indexedDBDelete = (id: number) => {
  const idb = window.indexedDB;
  return new Promise((resolve, reject) => {
    const dbOpen = idb.open("tableName", 1);
    dbOpen.onsuccess = () => {
      const db = dbOpen.result;
      const transaction = db.transaction("article", "readwrite");
      const tableNameDB = transaction.objectStore("article");

      const deleteRequest = tableNameDB.delete(id);

      deleteRequest.onsuccess = () => {
        transaction.oncomplete = () => {
          db.close();
          resolve(true);
        };
      };

      deleteRequest.onerror = (e) => {
        console.error("error : ", e);
        reject(e);
      };

      transaction.oncomplete = () => {
        db.close();
      };
    };
  });
};

삭제 함수를 통해 삭제를 진행하는데 만약 id =2 인 값을 삭제하면 아래와 같이 해당 id를 가진 컬럼이 삭제됩니다.

 

 

이상 간단하게 indexedDB사용법을 정리해봤습니다.

indexedDB를 메인으로 사용하기에는 다방면으로 무리가 있을 듯 하지만 그래도 로컬환경에나서 프론트 기준에서 백엔드 작업이 진행되지 않았을 때에는 필요한 데이터를 최대한 비슷한 환경에서 테스트해보기에는 좋을 것 같습니다.

 

잘못된 부분 지적은 언제나 감사합니다.

728x90
728x90

1편 글은 여기로!

https://kkt102.tistory.com/119

 

[React] 카카오맵 API 커스텀 해보기-1

따로 사이드 프로젝트와 회사 프로젝트를 진행하다보니 생각보다 카카오맵을 많이 사용하게되서 혹시라도 저와 비슷한 고민을 하는분들이 있지 않을까 싶어 작성하게 됬습니다. 절대로 100% 정

kkt102.tistory.com

 

저번 1편에 지도출력, 현재위치 랜더링, 마커, 마커변경에서 이어지는 글 입니다.

1.  마커는 표시됬고 현재위치 마커에서 특정 반경을 표시해주고싶은데...?

이번에는 마커 중심으로 도형을 그려보겠습니다!

저는 해당 기능을 마커 중심에서 특정 반경을 표시할 때 사용해봤습니다. 사실 얼마나 정확한지는 모르겠지만...

가장 기본적인 원형을 띄우는 코드입니다.

const KakaoMap = () => {
  const kakaoMaps = useRef(null);
  const [map, setMap] = useState(null);

const getKakao = () => {
   if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(function (position) {
        const mapContainer = document.getElementById("map");
        if (!map) {
          const mapOptions = {
            center: new window.kakao.maps.LatLng(
              position.coords.latitude,
              position.coords.longitude
            ),
            level: 4,
          };

          const map = new window.kakao.maps.Map(mapContainer, mapOptions);

          // MY POSITION MARKER
          const myPosition = new window.kakao.maps.LatLng(
            position.coords.latitude,
            position.coords.longitude
          );
          const myMarker = MyMarkerIcon,
            myMarkerSize = new window.kakao.maps.Size(20, 20),
            myMarkerOption = { offset: new window.kakao.maps.Point(0, 0) };
          const myMarkerPosition = new window.kakao.maps.MarkerImage(
            myMarker,
            myMarkerSize,
            myMarkerOption
          );
          const marker = new window.kakao.maps.Marker({
            map: map,
            image: myMarkerPosition,
            position: myPosition,
          });

          // USER RANGE CIRCLE
          const circle = new window.kakao.maps.Circle({
            center: new window.kakao.maps.LatLng(
              position.coords.latitude,
              position.coords.longitude
            ),
            radius: 300,
            strokeWeight: 2,
            strokeColor: "#6556FF",
            strokeOpacity: 0.2,
            strokeStyle: "solid",
            fillColor: "#6556FF",
            fillOpacity: 0.05,
          });

          circle.setMap(map);
          setMap(map);
        }
      });
    } else {
      console.log("내위치를 사용할 수 없어요.");
    }
  };

  useEffect(() => {
    getKakao();
  }, []);
  return (
     <div
       id="map"
       ref={kakaoMaps}
       style={{width: "100vw",height: "100vh}}
     ></div>
  );
};

export default KakaoMap;

일단 해당 코드는 내 위치의 마커+반지름 300m 의 반지름을 표시하는 코드입니다.

먼저 도형을 그리는 코드를 살펴보겠습니다!

 

먼저 center의 경우 도형의 중심을 지정해주는 옵션입니다. 샘플 코드에서는 위에 geolocation으로 받아온 접속한 사용자의 중심좌표로 지정해뒀습니다.

 

radius 는 원의 반지름을 m 단위로 입력해줄 수 있는 옵션입니다.

 

strokeWeight 의 경우는 원의 외부에 보여지는 라인의 두깨를 지정하는 옵션!

즉 border-width 값을 px 단위로 입력해줍니다.

 

strokeColor 은 말그대로 stroke의 색상을 지정해주는 옵션입니다. 

 

strokeOpacity 는 stroke의 투명도! 만약 해당 옵션을 아예 설정하지 않았다면 기본값인 1 로 설정됩니다.

 

strokeStyle 은 말그대로 border-style을 설정하는 옵션입니다.

 

fillColor 은 원형 내부의 색상을 설정하는 옵션입니다.

 

fillOpacity 는 원형의 투명도를 설정해주는 옵션입니다.

 

현재는 도형을 원형밖에 사용해보지 않았습니다만 카카오맵 API 가이드를 확인해보면 원형만 있는게 아니라

 

선이나 사각형, 다각형까지 그려줄 수 있습니다.

 

카카오맵 가이드 이동

각 그리는 도형마다 내부의 옵션값들이 조금씩 변경됩니다.

추후에 선, 사각형, 다각형을 다양한 방법으로 사용하는 방법을 포스팅하겠습니다!!!

 

2.  자 이번에는 여러개의 마커를 띄어주고 클러스터러도 적용하고 싶은데!!!

다음으로는 여러개의 마커를 띄운 후 클러스터러를 적용해보겠습니다.

단순하게 내 위치 중심의 좌표가 아닌 다양한 좌표에 마커를 띄우고 싶은 경우가 있습니다.

예를들어 키워드 검색을 통해 나온 결과들의 장소, 혹은 고정적으로 입력해놓은 좌표에 대한 마커와 같이

여러개의 마커를 띄우는 방법을 알아보겠습니다. 일단 채찍피티(?) 에 문의해본 결과

대한민국 한정 위도는 33.2491 ~ 37.8779 까지이며 경도는 126.9994 ~ 129.9993 이라고 답을 들었습니다!

 

위도나 경도값이 소수점 한자리만 바뀌어도 지도상에서는 위치가 많이 달라지기 때문에 확인이 필요합니다!

 

혹 원하는 지도상의 위도/경도값을 찾고싶은 경우는 아래의 사이트를 참고하시면 될 듯 합니다!!

 

http://map.esran.com/

 

위도경도찾기

위도경도찾기, IP 주소확인, 일출일몰 시간, 위도경도 정의

map.esran.com

https://www.findlatlng.org/

 

지도로 위도 경도 좌표 찾기

지도에 직접 마우스를 클릭하여 위도 경도를 확인 할 수 있습니다. 또한 검색창에 주소나 위치를 입력하면 검색된 결과로 지도로 이동하여 표시 됩니다.

www.findlatlng.org

 

자 그럼 일단 코드를 먼저 보겠습니다

 

const KakaoMap: React.FC = () => {
  const myKakaoMaps = useRef(null);
  const dummyData = useRecoilValue(dummyDateState);
  const [map, setMap] = useState(null);
  const [userLat, setUserLat] = useState(0);
  const [userLng, setUserLng] = useState(0);

  const getKakao = () => {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(function (position) {
        setUserLat(position.coords.latitude);
        setUserLng(position.coords.longitude);
        let locPosition = new window.kakao.maps.LatLng(
          position.coords.latitude,
          position.coords.longitude
        );
        const mapContainer = document.getElementById("map");
        if (!map) {
          const mapOptions = {
            center: new window.kakao.maps.LatLng(
              position.coords.latitude,
              position.coords.longitude
            ),
            level: 5,
          };

          const map = new window.kakao.maps.Map(mapContainer, mapOptions);
          const clusterer = new window.kakao.maps.MarkerClusterer({
            map: map,
            averageCenter: true,
            minLevel: 5,
            calculator: [99, 999, 9999],
            disableClickZoom: true,
            styles: [
              {
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
                width: "40px",
                height: "50px",
                paddingBottom: "22%",
                background: `url(${MapMarkerIcon}) no-repeat center center`,
                backgroundSize: "40px 50px",
                fontSize: "18px",
                fontWeight: 700,
                color: `let(--point-color)`,
              },
              {
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
                width: "50px",
                height: "60px",
                paddingBottom: "26%",
                background: `url(${MapMarkerIcon}) no-repeat center center`,
                backgroundSize: "50px 60px",
                fontSize: "18px",
                fontWeight: 700,
                color: `let(--point-color)`,
              },
              {
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
                width: "70px",
                height: "80px",
                paddingBottom: "26%",
                background: `url(${MapMarkerIcon}) no-repeat center center`,
                backgroundSize: "70px 80px",
                fontSize: "18px",
                fontWeight: 700,
                color: `let(--point-color)`,
              },
            ],
          });
          const individualMarkers: any = [];
          dummyData?.forEach((position) => {
            const MarkerSrc = MapMarkerIcon;
            const MarkerSize = new window.kakao.maps.Size(45, 55);
            const MarkerInfo = new window.kakao.maps.MarkerImage(
              MarkerSrc,
              MarkerSize
            );
            const marker = new window.kakao.maps.Marker({
              position: new window.kakao.maps.LatLng(
                position.location_lat,
                position.location_lon
              ),
              image: MarkerInfo,
            });

            individualMarkers.push(marker);
          });

          clusterer.addMarkers(individualMarkers);
          map.setMinLevel(2);
          map.setMaxLevel(5);
          map.setDraggable(false);
          setMap(map);
        }
      });
    } else {
      console.log("내위치 사용 불가");
    }
  };

  useLayoutEffect(() => {
    getKakao();
  }, []);
  return (
    <div
      id="map"
      ref={myKakaoMaps}
      style={{width: `100vw`, height: `100vh`}}
    ></div>
  );
};

export default KakaoMap;

 

일단 저는 dummyData라는 state 에 여러개의 마커를 띄우기 위한 정보를 저장시켜놨습니다.

사실은 마커를 생성할 때 필요한 위도, 경도 값만 있어도 되지만 제가 작업 할 떄 필요한 해당 가게의 주소와 명칭 등을 추가로 넣어준 상태입니다.

 

먼저 클러스터러를 설정해 주겠습니다!

기본적인 클러스터러  예제 또한 카카오맵 API 문서에 잘 나와있으니 참고하시면 좋을 것 같습니다!

https://apis.map.kakao.com/web/sample/basicClusterer/

 

          const clusterer = new window.kakao.maps.MarkerClusterer({
            map: map,
            averageCenter: true,
            minLevel: 5,
            calculator: [99, 999, 9999],
            disableClickZoom: true,
            styles: [
              {
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
                width: "40px",
                height: "50px",
                paddingBottom: "22%",
                background: `url(${MapMarkerIcon}) no-repeat center center`,
                backgroundSize: "40px 50px",
                fontSize: "18px",
                fontWeight: 700,
                color: `let(--point-color)`,
              },
              {
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
                width: "50px",
                height: "60px",
                paddingBottom: "26%",
                background: `url(${MapMarkerIcon}) no-repeat center center`,
                backgroundSize: "50px 60px",
                fontSize: "18px",
                fontWeight: 700,
                color: `let(--point-color)`,
              },
              {
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
                width: "70px",
                height: "80px",
                paddingBottom: "26%",
                background: `url(${MapMarkerIcon}) no-repeat center center`,
                backgroundSize: "70px 80px",
                fontSize: "18px",
                fontWeight: 700,
                color: `let(--point-color)`,
              },
            ],
          });
 
         clusterer.addMarkers(individualMarkers);
 

 

averageCenter 값은 boolean으로 되어있는데 일단 전 가이드에 맞춰서 true 로 설정해놨습니다.

공식문서에서는  해당 클러스터에 포함 된 마커들의 평균값을 클러스터 위치로 표시한다고 하는데 아마 false 로 설정하면 클러스터들의 위치를 임의로 설정해줄 수 있는 것 같습니다.

 

다음으로 minLevel 의 경우는 클러스터가 작동 될 최소한의 지도 크기를 설정해주면 됩니다. 전 5로 설정해놨기 때문에 지도 확대레벨이 4일 경우는 자동으로 클러스터가 적용되지 않고 모든 마커들이 각 좌표의 값에 보여지게 됩니다.

 

disableClickZoom  의 옵션은 클러스터를 클릭했을 때 지도가 확대될지를 설정해주는 값 입니다. 해당 값을 false 로 설정 할 경우 클러스터를 클릭/터치해도 지도가 확대되지 않습니다.

 

다음으로  calculator 의 값은 클러스터의 크기 구분 값 입니다. 해당 값들을 기준으로 스타일일나 문자를 지정해줄 수 있습니다. 제가 지정한 값에서는 1~99, 100~999, 1000~9999 의 클러스터러를 각각 다른 스타일, 텍스트로 표현할 수 있게됩니다. 저는 스타일만 따로 지정하였습니다.

 

그다음 스타일인데 스타이를 리터럴 작성법으로 표기해주며 각 스타일은 calculator 에 선언해준 값과 순서를 기준으로 적용됩니다. 즉 1번째 스타일은 1~99 에만 적용되고 2번째 스타일은 100~999... 이런식으로 적용됩니다.

 

그리고 마지막으로  clusterer.addMarkers(individualMarkers); 를 통해 클러스터러에 마커들을 추가 해줍니다.

 

해당 옵션에 대한 기본적인 설명은 아래의 카카오맵 API 가이드 문서를 참고하시면 좋을 것 같습니다!

 

https://apis.map.kakao.com/web/sample/chickenClusterer/

 

 

const individualMarkers: any = [];
dummyData?.forEach((position) => {
        const MarkerSrc = MapMarkerIcon;
        const MarkerSize = new window.kakao.maps.Size(45, 55);
        const MarkerInfo = new window.kakao.maps.MarkerImage(
            MarkerSrc,
            MarkerSize
        );
        const marker = new window.kakao.maps.Marker({
            position: new window.kakao.maps.LatLng(
                position.location_lat,
                position.location_lon
            ),
            image: MarkerInfo,
        });
    individualMarkers.push(marker);
});

 

일단 individualMarkers 라는 빈 배열을 만들어 줍니다. 그 다음 forEach문을 사용하여 dummyData라는 state를 풀어줍니다!

코드를 보시면 마커를 띄우기 위해 위도, 경도값만 사용되고 있습니다. 그 이외의 옵션은 기본적인 마커를 띄우는 방법과 동일합니다. 커스텀 마커를 띄울 떄 사용되는 이미지의 크기 및 이미지 주소 입니다.

그리고 individualMarkers  에 밀어넣어줍니다.

 

해당 코드가 실행 되면 아래와 같이 지도 레벨에 따라 클러스터와 마커가 표시됩니다.

클러스터 표시
마커 표시

3.  마커 클릭했을 때 뭔가 다른 이벤트를 걸어주는건???!

마커에 이벤트를 거는 방법은 조금 간단(?)합니다.

 

          dummyData?.forEach((position) => {
            const MarkerSrc = MapMarkerIcon;
            const MarkerSize = new window.kakao.maps.Size(45, 55);
            const MarkerInfo = new window.kakao.maps.MarkerImage(
              MarkerSrc,
              MarkerSize
            );
            const marker = new window.kakao.maps.Marker({
              position: new window.kakao.maps.LatLng(
                position.location_lat,
                position.location_lon
              ),
              image: MarkerInfo,
            });

// 추가되는 부분
			window.kakao.maps.event.addListener(marker, "click", function () {
              console.log(marker);
            });
// 추가되는 부분
            individualMarkers.push(marker);
          });

 

위와같은 코드처럼 forEach 부분에서 이벤트를 추가해주면 각 마커에 대하여 개별 이벤트를 적용시킬 수 있습니다!

 

마커 클릭 이벤트에 관한 정보 또한 카카오맵 API 가이드문서에 잘 나와있으므로 참고하시면 좋을 것 같습니다!

https://apis.map.kakao.com/web/documentation/#Marker_click

 

 

 

위의 이벤트를 통해 marker 정보를 찍어보면 아래와 같은 정보가 나옵니다!

 

다만 저 값들이 정확하게 어떤 것을 뜻하고 어떤 정보를 담고 있는지는 정확하게 파악하지는 못했습니다.

제게 필요한 정보가 아니다보니 그냥 넘어가기도 해서 그렇습니다..

 

아시는분은 댓글 달아주시면 감사하겠습니다!

 


일단 카카오맵 커스텀은 이정도까지만 하겠습니다.

 

저도 사실 혼자했다고하면 거짓말이고 채찍피티(?) 의 도움을 많이 받아서 진행했고 조금 더 수정을 거쳐 제가 원하는 동작을 하는 지도로 커스텀을 완료하였습니다!

이 글을 통해 조금이라도 도움이 되시면 좋겠습니다....!

 

지금까지 설명을 더 못하는 글 읽어주셔서 감사하고 언제나 잘못된 부분을 지적하는 댓글은 감사합니다!

728x90
728x90

따로 사이드 프로젝트와 회사 프로젝트를 진행하다보니 생각보다 카카오맵을 많이 사용하게되서

혹시라도 저와 비슷한 고민을 하는분들이 있지 않을까 싶어 작성하게 됬습니다.

 

절대로 100% 정확한 답이 아니지만 그래도 도움이 되면 좋겠네요.

 

1. 그래서 카카오맵 API 를 React에서 어떻게 사용한다고??!! (또다시 반복!)

https://kkt102.tistory.com/69

네... 일단 JavaScript Key도 발급 받아야하고 사용 할 URL도 등록해주고... 기본 작업은 동일합니다.

근데 카카오맵 API 가이드에는 스와이프처럼 다른 프레임워크 기준으로 가이드가 없다보니 

저는 처음에 삽질아닌 삽질을 많이 했었습니다.

나중에 와서 능력자 한분이 카카오맵 API를 React Module로 배포한 적이 있는 걸 찾긴했지만...

 

https://react-kakao-maps-sdk.jaeseokim.dev/

 

Hello from react-kakao-maps-sdk docs | react-kakao-maps-sdk docs

Description will go into a meta tag in <head />

react-kakao-maps-sdk.jaeseokim.dev

전 어느정도 작업을 끝낸 후 해당 라이브러리를 찾은지라 사실상 싹 밀고 다시 작업해야하는 상황이어서

해당 라이브러리를 사용해보지는 못했습니다...

 

2. 지도를 React 화면에 출력시켜보기! (리액트에 카카오맵 출력)

const KakaoMap = () => {
  const kakaoMaps = useRef(null);
  const [map, setMap] = useState(null);

  const getKakao = () => {
    const mapContainer = document.getElementById('map');
    if (!map) {
      const mapOptions = {
        center: new window.kakao.maps.LatLng(33.450701, 126.570667),
        level: 4,
      };
      const map = new window.kakao.maps.Map(mapContainer, mapOptions);
      setMap(map);
    }
  };
  useEffect(() => {
    getKakao();
  }, []);
  return <div id="map" ref={kakaoMaps} style={{ width: '100vw', height: '100vh' }}></div>;
};

export default KakaoMap;

해당 코드가 실행되면 

 

이런 아무표시도 없는 지도화면이 출력됩니다!

만약 프로젝트를 최초 성생 후 해당 컴포넌트로 이동했을 떄 지도가 중첩되어 뜬다면 

해당 프로젝트의 root 경로에서 index.js 혹은 index.ts 파일에 선언되어있는

Strict 모드를 비활성화 해주세요.

 

해당 파일에서 <React.StrictMode> 와 </React.StrictMode> 를 주석처리해주셔도 됩니다!

 

간단하게 카카오맵 옵션을 설명해드리면 

 

const mapOptions = {
        center: new window.kakao.maps.LatLng(33.450701, 126.570667),
        level: 4,
      };

해당 부분은 카카오맵 최초 로딩 시 지도의 중심 좌표(위도,경도) 와 지도의 비율을 설정하는 부분입니다.

일단은 기본적으로 위도와 경도를 카카오맵 가이드에 나와있는 좌표로 설정해줬으며

 

확대/축소 레벨은 4로 설정했습니다.

레벨은 1 ~ 13 까지 있으며 숫자가 낮을수록 확대된 비율로 지도가 생성됩니다.

 

const map = new window.kakao.maps.Map(mapContainer, mapOptions);
      setMap(map);

그다음 실제 지도를 생성해주는데 이 때 지도가 표시 될 앨리먼트는 'mapContainer'이며 해당 지도의 옵션값은 'mapOptions' 를 사용하게 되며 해당 값은 setMap(map) 을통하여 state 를 업데이트 해주는 방식입니다.

 

그리고 React Component를 업데이트 리랜더링 시켜주는 useEffect를 사용하여 지도를 호출합니다!

 

3. 이번에는 현재위치로 지도를 나오게 하고싶은데? (현재위치 랜딩)

간단하게 지도를 출력했으니 이번에는 임의의 위치가 아닌 내가 현재 접속한 위치를 중심으로 지도를 출력해야겠습니다!

현재 접속한 위치를 알기 위해선 geolocation을 사용해야합니다.

 

https://developer.mozilla.org/ko/docs/Web/API/Geolocation_API

 

Geolocation API - Web API | MDN

Geolocation API는 사용자의 동의 하에 웹 애플리케이션에서 위치 정보에 접근할 수 있는 API입니다. 개인정보 보호를 위해, 브라우저는 위치 정보를 제공하기 전에 사용자에게 위치 정보 권한에 대

developer.mozilla.org

geolocation은 위의 문서를 통해 확인하실 수 있습니다!

const KakaoMap = () => {
  const kakaoMaps = useRef(null);
  const [map, setMap] = useState(null);

  const getKakao = () => {
  	if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(function (position) {
        const mapContainer = document.getElementById('map');
        if (!map) {
          const mapOptions = {
            center: new window.kakao.maps.LatLng(
              position.coords.latitude,
              position.coords.longitude
            ),
            level: 4,
          };
          const map = new window.kakao.maps.Map(mapContainer, mapOptions);
          setMap(map);
        }
      })
    } else {
      console.log('내 위치를 사용할 수 없어요.')
    }
  };
  useEffect(() => {
    getKakao();
  }, []);
  return <div id="map" ref={kakaoMaps} style={{ width: '100vw', height: '100vh' }}></div>;
};

export default KakaoMap;

 

이번에는 해당 컴포넌트로 접속하면 아래와 같은 권한요청관련 Alert가 활성화 됩니다.

당연하게 '허용' 을 눌러줍니다!

그러면 아까와 같은 임의의 위치 지도가 아닌 현재 접속한 위치 중심으로 지도가 출력됩니다.

 

4. 이번에는 내 위치를 지도에 마커로 표시해야겠어! (마커생성)

이제 사용자가 웹페이지를 통해 해당 컴포넌트가 활성화되면 지도가 뜨고 해당 지도의 중심좌표는 현재 위치를 기반으로 지도가 뜨게됩니다. 하지만 지도가 뜨긴 하는데... 뜨는김에 마커로 현재 위치까지 딱 찍어주면 더 좋을 것 같아요.

const KakaoMap = () => {
  const kakaoMaps = useRef(null);
  const [map, setMap] = useState(null);

  const getKakao = () => {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(function (position) {
        const mapContainer = document.getElementById("map");
        if (!map) {
          const mapOptions = {
            center: new window.kakao.maps.LatLng(
              position.coords.latitude,
              position.coords.longitude
            ),
            level: 4,
          };
          const map = new window.kakao.maps.Map(mapContainer, mapOptions);
          const myPosition = new window.kakao.maps.LatLng(
            position.coords.latitude,
            position.coords.longitude
          );

          const marker = new window.kakao.maps.Marker({
            map: map,
            position: myPosition,
          });
          setMap(map);
        }
      });
    } else {
      console.log("내 위치를 사용할 수 없어요.");
    }
  };
  useEffect(() => {
    getKakao();
  }, []);
  return (
    <div
      id="map"
      ref={kakaoMaps}
      style={{ width: "100vw", height: "100vh" }}
    ></div>
  );
};

export default KakaoMap;

 

이번엔 마커를 표시해주기 위해 geolocation을 통해 위도와 경도를 알아낸 후 지도를 생성하는 것이 아니라

해당 위도와 경도 값을 받아온 후 마커를 띄울 위치로 지정해줍니다.

        const myPosition = new window.kakao.maps.LatLng(
          position.coords.latitude,
          position.coords.longitude
        );

먼저 현재 위치를 지정하기 위해 myPosition 변수에 위도와 경도를 선언해줍니다.

 

        const marker = new window.kakao.maps.Marker({
          map: map,
          position: myPosition,
        });

그리고 마커를 생성해주는 변수 선언...

 

이제 지도에 현재 접속한 위치에 기본 마커가 표시되고있습니다.

 

5. 기본마커는 안이쁘니까 다른 이미지로 바꾸자 (마커 커스텀)

마커를 띄우면 카카오맵에서 자주 봤던 그 기본마커 이미지가 표시되는 것을 확인할 수 있습니다.

하지만 만약 지도를 사용하면서 해당 페이지나 전체적인 분위기와 마커가 안맞을 경우가 있습니다.

이 때 마커를 다른 이미지로 바꿔줄 수 있습니다.

마커를 다른 이미지로 바꾸는 것은 기존 마커를 추가하는 코드와 크게 다르지 않습니다

const KakaoMap = () => {
  const kakaoMaps = useRef(null);
  const [map, setMap] = useState(null);

  const getKakao = () => {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(function (position) {
        const mapContainer = document.getElementById("map");
        if (!map) {
          const mapOptions = {
            center: new window.kakao.maps.LatLng(
              position.coords.latitude,
              position.coords.longitude
            ),
            level: 4,
          };
          const map = new window.kakao.maps.Map(mapContainer, mapOptions);
          const myPosition = new window.kakao.maps.LatLng(
            position.coords.latitude,
            position.coords.longitude
          );

          const myMarker = 'image url',
            myMarkerSize = new window.kakao.maps.Size(20, 20),
            myMarkerOption = { offset: new window.kakao.maps.Point(0, 0) };
          const myMarkerPosition = new window.kakao.maps.MarkerImage(
            myMarker,
            myMarkerSize,
            myMarkerOption
          );
          const marker = new window.kakao.maps.Marker({
            map: map,
            image: myMarkerPosition,
            position: myPosition,
          });
          setMap(map);
        }
      });
    } else {
      console.log("내 위치를 사용할 수 없어요.");
    }
  };
  useEffect(() => {
    getKakao();
  }, []);
  return (
    <div
      id="map"
      ref={kakaoMaps}
      style={{ width: "100vw", height: "100vh" }}
    ></div>
  );
};

export default KakaoMap;

 

위의 코드는 전체코드입니다!

기존 마커가 추가 된 코드에서 마커 이미지에 대한 변수만 추가된 상태입니다!

 

const myMarker = 'image URL',
   myMarkerSize = new window.kakao.maps.Size(20, 20),
   myMarkerOption = { offset: new window.kakao.maps.Point(0, 0) };
 
const myMarkerPosition = new window.kakao.maps.MarkerImage(
   myMarker,
   myMarkerSize,
   myMarkerOption
);
 
const marker = new window.kakao.maps.Marker({
    map: map,
    image: myMarkerPosition,
    position: myPosition,
});

 

일단 myMarker 부분에서 다른 마커로 교체하기위한 이미지 src 를 추가해주고

myMarkerSize 에는 사용할 마커의 크기!

myMarkerOption에는 해당 마커가 좌표에서 위치할 x,y 축!

그리고 myMarkerPosition 의 변수에 선언한 옵션을 넣어줍니다!

마지막으로 Marker 에 image: myMarkerPosition 을 선언해주면 마커이미지가 교체되서 나옵니다!

 

이 때 사용 할  이미지의 src는 위의 샘플처럼 일반 문자열로 선언해주거나

이미지파일을 import 해서 선언해줘도 동일하게 적용됩니다!

 

일단은 제가 까먹지 않기 위해 두서없이 적게 된 글입니다...

따끔한 지적은 언제나 환영합니다! 조만간 도형을 표시하거나 마커에 특정이벤트, 여러개 마커 표시 등도 올려보겠습니다.

 

읽어주셔서 감사합니다!

 


❗❗❗2탄 바로가기❗❗❗

2024.05.20 - [Programming/React] - [React] 카카오맵 API 커스텀 해보기-2

 

[React] 카카오맵 API 커스텀 해보기-2

1편 글은 여기로!https://kkt102.tistory.com/119 [React] 카카오맵 API 커스텀 해보기-1따로 사이드 프로젝트와 회사 프로젝트를 진행하다보니 생각보다 카카오맵을 많이 사용하게되서 혹시라도 저와 비슷

kkt102.tistory.com

 

728x90
728x90

- Nestjs란?

Nestjs란... 공식 홈페이지에서는 아래와 같이 나와있다.

 

Introduction

Nest (NestJS) is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with and fully supports TypeScript (yet still enables developers to code in pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).

Under the hood, Nest makes use of robust HTTP Server frameworks like Express (the default) and optionally can be configured to use Fastify as well!

Nest provides a level of abstraction above these common Node.js frameworks (Express/Fastify), but also exposes their APIs directly to the developer. This gives developers the freedom to use the myriad of third-party modules which are available for the underlying platform.

 

구선생님의 힘을 빌려서 발변역을 해보면

 

Nest (NestJS)은 효율적이고 확장 가능한 Node.js 서버 측 응용 프로그램을 구축하기 위한 프레임워크입니다. Progressive JavaScript를 사용하며 TypeScript를 완벽하게 지원하며 개발자가 순수 JavaScript로 코드를 작성할 수도 있습니다. 또한 객체 지향 프로그래밍(OOP), 함수형 프로그래밍(FP), 및 함수형 반응형 프로그래밍(FRP)의 요소를 결합합니다.

Nest는 Express(기본값)와 같은 견고한 HTTP 서버 프레임워크를 내부적으로 사용하며 선택적으로 Fastify를 구성하여 사용할 수도 있습니다.

Nest는 이러한 일반적인 Node.js 프레임워크(Express/Fastify) 위의 추상화 수준을 제공하지만 개발자에게 직접적으로 이러한 프레임워크들의 API를 노출시킵니다. 이로써 개발자는 기본 플랫폼을 위한 다양한 서드파티 모듈을 자유롭게 사용할 수 있습니다.

 

즉 백앤드 서버를 JavaScript로.... 프론트앤드를 괴롭힐 수 있다는 것이다!

 

- Nestjs 설치하기

일단 nestjs 를 사용하기 위해선 nestjs를 설치해야 합니다. nestjs는 node.js 기반으로 돌아가기때문에 당연히 node.js가 설치되어 있어야 합니다.

 

node가 설치되어있다는 전재로 nestjs를 설치하겠습니다.

 

터미널에 아래의 명령어를 입력해줍니다.

 

npm i -g @nestjs/cli

 

자 이제 nestjs 설치가 끝났습니다!  그래도 혹시 모르니 nest -v 를 입력해서 버전을 확인합니다.

 

설치가 끝났습니다.

 

- Nestjs 프로젝트 생성하기

이제 설치를 끝냈으니 프로젝트를 생성해보겠습니다. 아래의 명령어를 통해 프로젝트를 생성합니다.

nest new 프로젝트명

 

해당 명령어를 입력하면 아래의 이미지와 같이 패키지 매니저를 선택하라고 나옵니다. 전 yarn 을 사용할 예정이니 키보드 화살표를 사용하여 패키지 매니저를 yarn 으로 설정 후 프로젝트를 생성합니다!

 

기본적인 설치라서 상당히 빠르게 프로젝트가 생성됩니다!!!

 

 

프로젝트 실행은 yarn 이므로 yarn start 를 입력하면 nest 프로젝트가 실행됩니다.

728x90

'Programming > Nestjs' 카테고리의 다른 글

[Nestjs] .entity.ts 에 사용되는 데코레이터 종류  (1) 2024.10.09

+ Recent posts