728x90

데이터 마이그레이션 후 갑자기 목록 조회가 안되는 현상이 발생했다.

log를 확인해보니

라는 에러가 발생했다.

???

누구세요

에러 로그를 읽어보면 조회를 하는데 조회할때 할당된 패킷은 4,194,304byte인데 11,594,289byte의 패킷이 조회되서 에러 띄웠다고 한다.

그럼 Mysql의 패킷 단위를 변경하면 되는 것이 아닐까??

찾아보니 max_allowed_paket이라는 설정 값을 변경해주면 된다고 한다.

그럼 max_allowd_paket은 뭔가??
말 그대로 데이터 베이스 서버에서 한번에 전송되는 최대 데이터 패킷 크기를 결정하는 설정이라고 한다.

즉 데이터를 조회 혹은 등록 할 때 주고 받을 수 있는 데이터의 양이라고 보면 편할 것 같다.

우선 현재 max_allowd_paket 이 얼마로 설정되어 있는지 확인해보자.

show variables where Variable_name = 'max_allowed_packet';

확인 후 본인이 늘리고 싶을 만큼 늘려보자 ( 주의 패킷이 주고받는 데이터 양을 의미하는데 생각없이 너무 늘려버리면 그것은 메모리 누수로 이어질 수 있을 거 같다. )

SET GLOBAL max_allowed_packet =12000000

늘리고 난 이후 Mysql의 설정을 적용해 주도록 하자

FLUSH PRIVILEGES

이후 확인을 해보면 패킷 최대 용량이 변경 된 것을 확인할 수 있을 것이다.

위의 방법은 command로 적용하는 방법이였고,
설정 파일을 수정하려면 mysql 설정파일 my.cnf또는 my.init에서

max_allowed_packet=16M

로 설정을 추가 혹은 변경해주면 될 것이다.

오늘 하루도 화이팅하자...야근 하이....

728x90

'Error' 카테고리의 다른 글

[Error] java.util.LinkedHashMap cannot be cast to Class  (0) 2024.04.16
728x90

목차

  1. 이유
  2. 사용방법
  3. 후기

이유

local에서 개발하고 테스트 하다보면
여러개의 데이터 베이스를 생성 혹은 처음 프로젝트를 시작할 때 좀 깔끔한 상태로 시작하고 싶다는 생각을 할 수 있다.

개인적으로 이런 생각을 가지게 된 데에는 포트 충돌이 가장 큰 원인이였다.

예를 들어 Mysql을 사용하다 MariaDB를 사용하게 되어 설치를 하려하는데 포트가 충돌 되는 현상이 발생하였다.

무슨일일까?

Mysql을 설치할 당시 기본 포트로 3306번을 할당해 주게 되는데 MariaDB또한 3306포트를 기본으로 할당해주어 포트 충돌이 생긴 것이다.

물론 새로 설치하는 MariaDB를 3307로 바꿔 설치하면 되지만,
필자 처럼 특정 이유 없이 여러 포트에 생기는 것을 별로 안좋아하는 사람들도 있을 수 있다.

그래서 그때 부터 고민을 하게 되었는데

어차피 여러 서비스를 image로 만들어 container에 띄우게 되면 container만 갈아 치우는 느낌으로 하나의 포트만 두고 사용할 때만 꺼내 쓸 수 있는 데이터 베이스가 되지 않을 까 였다.

해서 이번 글에서는 위의 해결 방법에 대해 다루려 한다.

사용방법

우선 Docker를 설치해야하는데 이는 다음 글을 참고 하기 바란다.

Docker 설치하는 방법

Docker를 설치하였으면

이제 본인이 사용할 Image를 다운 받으면 된다.

1. 본인이 사용할 image를 검색한다.

우리가 데이터 베이스 이미지를 사용하기 위해선 docker hub에서 지원해주는 image를 다운받아 사용해야하는데 그 첫번째로 사용할 image를 검색하는 것이다.

docker search ${검색할 이미지 명}
ex) docker search mysql

#### 2. 검색한 image를 다운받는다.
>```bash
docker pull ${다운받을 이미지 명}
ex) docker pull mysql

3. image를 container로 실행시킨다.

--name: 실행시킬 container의 이름을 지정
-- MYSQL_ROOT_PASSWORD : container를 실행할 때 mysql의 root계정의 비밀번호를 지정
-- 3306:3306 mysql을 port 3306으로 설정 하여 실행

docker run --name mysql-container -e MYSQL_ROOT_PASSWORD=<password> -d -p 3306:3306 mysql:latest

#### 4. Docker container 시작/중지/정지
>```bash
# Docker 컨테이너 중지
$ docker stop ${container명}
# Docker 컨테이너 시작
$ docker start ${container명}
# Docker 컨테이너 재시작
$ docker restart ${container명}

4. 실행중인 Docker container 접속

docker exec -it ${container명} bash

#### 5. Mysql 접속
>```bash
  mysql -u ${mysql계정아이디} -p ${mysql비밀번호}

위 과정을 거치면 정상적으로 사용할 수 있다.

후기

업무상 본인 노트북을 가지고 다니며 업무용, 공부용, 프로젝트용으로 한번에 사용하다 보니 여러 데이터 베이스를 사용하는 경우가 많이 생기게 된다.

이때 헷깔리지 않고 잘 관리를 하기 위해 고민하였고 사용하고 있는데 확실히 편하다.

개인적으로 여러개를 동시에 실행시키면 노트북이 버벅 거릴 수도 있는데 솔찍히 말하면 컴퓨터 성능이 좋아 아직 그정도의 불편함은 느끼지 못하였고,
솔찍히 편하고 좋다.

한줄평

굉장히 편한거 같고 사용하는 데이터 베이스에 맞춰 컨테이너만 갈아 끼워주면 되서 좋다!!

728x90

'Server' 카테고리의 다른 글

SSE란? - Server Sent Events  (2) 2024.04.18
REST API란?  (0) 2024.04.14
[Docker] Docker 설치하기  (0) 2024.04.08
[Docker] Docker란 무엇인가?  (2) 2024.04.08
NATS란?  (0) 2024.04.08
728x90


이번 글에서는 Docker시리즈의 기본 Docker를 설치하는 방법에 대해 설명해보려한다.

Docker가 무엇인지에 대해 알고 싶으면 아래 링크를 참고하기 바란다.

Docker란 무엇인가?

사실 Docker공식 사이트에서 확인하면 되지만 그래도 필요한 분이 계실 거 같아 복습할겸 글을 써본다.

우선 설치 환경은 이렇다

OS: Ubuntu 22.04

따로 Kubernetes와 같은 것들은 따로 설치한다는걸 제외한 가정이기 때문에 가볍게 시작하겠다.

  1. apt-get을 최신으로 업데이트를 해준다.
  2. apt-get update
  3. apt-get install ca-certificates curl gnupg
  4. install -m 0755 -d /etc/apt/keyrings
  5. curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
  6. sudo chmod a+r /etc/apt/keyrings/docker.gpg
  7. echo \
    "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
    "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
    sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
  8. 위 설정을 토대로 Docker을 받기 위해 apt-get 을 업데이트 시켜준다.
  9. sudo apt-get update
  10. docker 관련 패키지를 다운받는다.
  11. sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  12. docker 실행 docker run 만 작성해도 정상적으로 작동한다.
  13. sudo docker run hello-world
  14. docker가 정상적으로 작동 중인지 확인한다.
  15. systemctl status docker

참고

docker 설치 - https://docs.docker.com/desktop/install/ubuntu/

728x90
728x90


서버 개발자라면 한 번쯤 들어 보았을 Docker 과연 Docker가 무엇인가에 대한 호기심으로 인해 글을 작성한다.

Docker란?

Docker는 애플리케이션을 개발, 제공 및 실행하기 위한 개방형 플랫폼이라고 할 수 있다.

이게 뭘까??

단순하게 애플리케이션을 개발, 제공 및 실행하기 위해서는 그냥 실행하면 되지 않을까란 생각을 할 수도 있다.

이를 이해하기 위해서는 앞서 Docker의 Container가 왜 나왔는지 역사를 알아볼 필요가 있다.

Container 탄생기

서비스를 실행하기 위해서는 하나의 서버 할당은 당연한 수순이라 바라볼 수 있다.

이때 가정을 하나 해보도록 하자.

서비스를 배포하기 위해 하나의 서버를 할당하였는데 서비스를 배포하고도 서버의 성능이 많이 남는다고 가정하도록 하겠다.

이때 해당 서버에 또 다른 서비스를 배포하고 싶은데 이때 같은 서버를 사용할 경우 기존 서비스의 설정과 추가할 서비스의 설정이 충돌이 날 경우
개발자는 두 서비스 중 어떤 서비스를 서버에 배포할 것인지 고민할 수밖에 없다.

이렇듯 하나의 서버에 여러 서비스를 할당하기 위해 탄생한 것이 "서버 가상화"다.

서버 가상화는 물리적 서버 호스트에서 여러 개의 서버 운영 체제를 게스트로 실행할 수 있는 소프트웨어 아키텍처다.


위와 같이 하나의 서버에 n 개의 VM을 설치하여 각각 OS를 할당하여 서비스 간의 연결을 끊어버리는 방법으로 나왔다.

하지만 위의 방식을 보면 뭔가 무겁다는 생각이 들지 않나?

그렇다 서비스를 Host OS에 영향이 가지 않게 나눠 Guest OS로 서비스를 실행하였지만 각각의 VM OS를 구동하기 위한 파일들을 가지게 됨으로써 Host 서버의 메모리를 훨씬 잡아먹는 것이다.

그러면서 우리는 고민을 하게 될 텐데
서버 메모리가 부족해지면 어떻게 해야 할까??

서버를 더 좋은 서버를 구매하여 메모리를 늘릴까??
하지만 위 방법을 사용할 경우 만약 서비스를 구동하는 메모리를 초과한다면 또 메모리 낭비로 이뤄지게 된다.

그러면 OS를 따로 띄우는 것이 아니라 하나의 OS 위에 배포되는 서비스의 환경을 분리하면 되지 않을까?

이렇게 탄생한게 "Container"다.

Container는 위와 같이 Guest OS를 가지고 있지 않은 것을 확인 할 수 있는데,
이는 Linux 자체 기능인 chroot, namespace, cgroup을 사용하여 프로세스 단위의 격리환경을 구축하기 때문이다.

Container에는 Application을 구동하는데 필요한 라이브러리 및 실행파일만 존재하여 이미지로 만들었을 때 가상머신에 비해 이미지 용량이 대폭 주는 효과를 누릴 수 있다.

그럼 Container는 무엇인가??

Container란?

앞서 Container에는 Application을 구동하는데 필요한 라이브러리 및 실행파일만 존재한다고 하였다.

그리고 Container는 프로세스 단위의 격리환경이다.

이처럼 Container는 격리된 환경을 제공하며 프로세스의 생명주기를 관리하고있다.

즉 독립된 프로세스 개발환경을 보장받을 수 있는 환경 그것에 바로 Container라고 볼 수 있다.


그럼 Container는 Docker인가??

답은 아니요 다.

Container는 Docker에서 관리하는 프로세스의 단위 일 뿐 Docker가 Container라고 하기엔 너무 많은 기능이 Docker에 포함되어있다.

이제부터는 그 기능들에 대해 알아보겠다.

Docker의 기능은 무엇이 있을까?

크게 4가지로 볼 수 있는데

  1. 컨테이너 관리
  2. 이미지 관리
  3. 볼륨 관리
  4. 네트워크 관리

등이 있다.

하지만 위는 엄청 간략하게 말한것이고 어떤 기능들을 제공해 주는지 확인해보자!

이 사진은 Docker에서 어떤 기능을 지원해주는 지 확인할 수 있다.

Dockerd

dockerd는 container를 지속적으로 관리하는 백그라운드 프로세스(docker daemon)이다.

Docker API - Docker Client의 명령어를 /var/run/docker.sock unix소켓을 통해 API를 호출하여 dockerd가 작업을 수행한 이후 Client에게 Response를 return.

Docker CLI - Docker Command Line Interface

Storage mgmt - Container가 사용하는 파일 및 이미지 데이터를 효과적으로 관리하며, 컨테이너의 생성, 실행, 중지 및 제거등과 관련된 모든 작업에 관여한다.

libnetwork - 컨테이너의 네트워크 설정과 관련된 작업을 처리.

SwarmKit - Docker의 오케스트레이션 도구 즉 여러대의 호스트에서 컨테이너를 관리하고 조절하는기능을 제공.

  1. 서비스 디스커버리와 로드 밸런싱
  2. 서비스 복제 및 스케일링
  3. 다양한 네트워킹 옵션
  4. 상태 관리와 롤링 업데이트
  5. 보안 및 권한 관리

BuildKit - Docker 이미지를 빌드할때 사용하며 기존 docker build보다 더 빠르고 더 많은 기능과 유연성을 제공한다.

Docker Content Trust - Docker이미지의 보안을 강화하는 기능을 제공한다.

Image mgmt - Storage mgmt가 Container를 관리하면 Iamge mgmt는 Image를 관리한다.
기본적으로 Image다운로드 및 업로드, 빌드, 생성, 버전관리, 검색, 삭제 등의 기능들을 제공한다.


마무리

이로써 기본적인 Docker에 관해 알아보았다.

결국 Docker이미지 처럼 여러 container들을 관리하는 고래가 Docker라고 보면 될거 같다.

비록 하나하나 깊게 다루지 못했지만 기본적인 기능으로만 보았을때 생각보다 제공해주는 기능이 많아 좀더 열심히 공부해야 할거 같다.

한줄평

기본 설정만 잘해두면 서비스들을 배포하고 관리하는데 편하다!!


참고
https://docs.docker.com/get-started/overview/
https://www.youtube.com/watch?v=IiNI6XAYtrs

728x90
728x90


프로젝트를 진행하다보면
"A의 값이 없을 때 B의 값을 보여주세요!!"를 볼 수 있습니다.

이떄 Typescript 를 사용해보신 분들이라면 아시겠지만 ||??를 봤을 것이다.

어떻게 사용하는지는 알텐데 정확히 어떤 점이 다른지가 궁금할 수 있는데,
이 글에서는 그 각각 어떤 효과를 내고, 어떤 의미를 가지는지를 알아보도록 하겠다.

Null병합 연산자( ?? )

개념

Nullish Coalescing Operator으로 불리며 피연산자가 null이거나 undefined라면 해당 피연산자의 대체 값을 사용하는 연산자다.

사용법

const text = null;

console.log(text ?? '값이 없습니다.');

// 출력값 = '값이 없습니다.'

특징

  1. 왼쪽에 있는 피연산자가 null이거나 undefined인 경우에만 false로 인식
  2. 논리연산자 OR(||) 연산자의 특수한 경우이다.
  3. 연산자들중 우선순위가 5번째로 낮으며, ||보다 한단계 낮은 우선순위를 가진다.
  4. AND(&&)와 OR(||)들과 직접 결합하여 사용 못한다.
  5. false로 인식될 수 있는 값들을 false로 처리하지 않는다. ( ex: 0, '' )

논리연산자 OR ( || )

개념

Logical OR으로 불리며 피연산자들 사이에 연결하여 사용하며,
앞의 피연산자가 false인 경우 뒤의 피연산자를 검사, 해당 피연산자가 true인 경우 해당 값을 사용.

boolean과 사용할 경우 boolean값을 반환하지만, 다른 자료형의 값을 사용할 경우 boolean값이 아닌 값을 반환한다.

사용방법

const text = '';

console.log(text || '값이 없습니다.');

// 출력값 = '값이 없습니다.'

특징

  1. 왼쪽에 있는 피연산자의 논리가 false인 경우 우측 피연산자를 출력
  2. false로 인식하는 값은 boolean, '', "", 0, undefined, Nan이 있다

후기

이 글을 작성하게 된 이유는 프로젝트 진행중 ??연산자를 사용한 부분에서 error가 발생하여 해당 문제를 해결하기위해 알아보는 와중 궁금해서 찾아보았다.
두개의 연산자가 서로 비슷한 출력을 한다 하더라도,
세세하게 보면 출력되는 값이 다르다는 것을 알 수 있다.

이로써 두 연산자를 분리하여 적재저소에 잘 사용하길 바란다.

일단 나부터...

728x90

'Front > TypeScript' 카테고리의 다른 글

[React] ImageUpload 여러 기능들  (0) 2024.04.08
728x90


Image를 업로드 한다 할 때 우리는 여러가지 기능들을 기대하게 됩니다.

미리보기 기능이 있으면 올린 파일이 어떻게 보이는지 미리 알 수 있어서 좋고, Drag Drop기능이 있으면 파일을 보다 편하게 업로드 할 수 있고, 이미지 복사하여 붙여넣기 기능이 있다면 더욱 편하게 업로드 할 수 있을 것입니다.

이렇게 Image혹은 파일을 업로드 하는 것을 우리는 일상속에서 많이 찾아볼 수 있습니다.

그리하여 이번에는 Image를 업로드 할 때 많이 접할 수 있는 미리보기, Drag Drop, Copy Paste기능을 정리하여 보겠습니다.


Preview


이미지 미리보기는 요즘 이미지 업로드라면 대부분이 가지고 있는 기능이라 생각합니다.

사실 이미지를 업로드 할때 미리보기를 구현하는 것은 매우 쉬운일입니다.

이미지를 업로드하게 된다면 이미지 파일을 업로드 할 것이고, 우리는 그 이미지를 그냥 화면에 뿌려주기만 하면 되는 것이기 때문입니다.

위와 같이 이미지를 업로드 할경우 우리는 event를 통해 file객체를 얻을 수 있습니다.

이때 file객체를 읽어 화면에 보여주기 위해서는 FileReader Object를 사용하였습니다.
이 FileReader는 비동기로 작동하는 File을 읽을 수 있도록 해주는 기능을 제공해줍니다.

FileReader는 file를 읽어들이게되면 읽을 수 있는 정보를 result에 담아주게 되는데 이를 저장하였다가 <img/> 태그의 src속성에 넣어주면 위 Gif처럼 이미지를 미리보여줄 수 있습니다.

// 파일을 읽어 result데이터를 뽑아내는 코드
const reader = new FileReader();
reader.readAsDataURL(image);
reader.onloadend = () => {
  if(reader.result && typeof reader.result === 'string') setImageReaderPath(reader.result);
}

Drag Drop


사용자가 편하게 이미지를 업로드 시킬 수 있게 도와주는 기능인 Drag Drop기능입니다.
이 기능은 우선 어떤 목적을 가지고 사용할 것인지가 중요할 것 같습니다.

드래그 할 수 있는 영역을 지정해 두고, 해당 필드에만 업로드 할 수 있게 하거나,
원하는 영역을 잡아 해당 영역을 드래그 드랍이 가능한 영역으로 잡는 것 입니다.

똑같은 것 아닌가??라고 생각하실 수 있지만,
코드 상으로 들어가게 되면 조금 달라진다는 것을 알 수 있습니다.

만약 드래그할 수 있는 영역을 따로 지정하여둔다면 코드 상에서 특별한 어려움 없이 해당 영역에만 이벤트 처리를 하면 됩니다.

다만 드래그를 할 수 있는 영역을 지정하지 않고, 전체영역과 같이 영역을 선택한다면, leave event를 조심해야할 것 입니다.
이유는 DragLeave이벤트는 본인 태그를 벗어나버릴 경우 호출되며, 이는 본인 태그의 자식태그로 마우스가 들어가게 되면 해당 이벤트가 발생하여 저와 같이 화면에 회색화면을 띄워주고 싶다 할 경우 문제가 생길 수 있기 때문입니다.

이를 해결하기 위해 저는 Drag영역과 DragLeave영역을 따로 설정하였습니다.

<div
  className={'image_upload_form'}
  onDragEnter={handleDragStart}
  onDrop={handleFileSelect}
  onDragOver={handleDragOver}
>
  {isDragging ? (
    <div
      className="fileDropZone"
      onDragLeave={handleDragLeave}
      >
      <div
        style={{display: "flex"}}
        >이미지를 업로드 해주세요.
      </div>
    </div>
  ) :
  selectImage ? <img src={selectImagePath} alt={'image'}/> : <span>파일 업로드</span>
  }

</div>

위와 같이 DragEnter즉 드래그를 인지하는 영역에 도달하면 isDragging을 활성화 하여 화면을 fileDropZone으로 덮어 드래그하는 도중 자식 필드를 만나 오류상황이 발생하는 경우를 최소화 하였습니다.

앞으로 해결해 나아가야할 부분이기도 하지만 좀더 깔끔하게 영역을 설정할 수 있도록 수정할 예정입니다.

또한 Drag Drop의 경우 웹 브라우저 상에 event가 걸려있어 해당 이벤트들을 중지 시켜주지 않으면 새 탭이 생기며 이미지가 띄워지는 상황이 발생하게 됩니다.

const handleFileSelect = (event: React.DragEvent<HTMLDivElement>) => {
  // console.log('작동?')
  event.preventDefault();
  event.stopPropagation();
  const file = event.dataTransfer.files.item(0);
  if(!file) {
    setIsDragging(false);
    return;
  }
  setSelectImage(file);
  const reader = new FileReader();
  reader.readAsDataURL(file);
  reader.onloadend = () => {
    if (reader.result && typeof reader.result === 'string') setSelectImagePath(reader.result);
  }
  setIsDragging(false);
};

위 코드 중에서 가장 중요한 부분이 2개 있는데, 이는 event에 설정되어있는 고유 동작을 실행하지 않는 설정인 event.preventDefault()event.stopPropagation()입니다.

event.preventDefatul()는 고유적으로 설정되어있는 동작들을 실행하지 않겠다라는 의미를 가지고 있습니다.

고유기능이라함은 a태그로 보았을 때 a태그를 클릭하였을 때 페이지 이동이 되는 고유 기능이 존재하지만 event.preventDefatul()를 사용하여 해당 고유기능을 사용하지 않게 되어 페이지 이동이 되지 않게 하는 것 입니다.

event.stopPropagation()는 클릭 이벤트가 발생시 상위 엘리먼트들에게로 이벤트가 전파되는 것을 방지하는 것 입니다.

쉽게 지금일어나는 일은 여기서만 해결할께요로 봐주면 좋을 것 같습니다.


Copy Peste


복사 붙여넣기 기능입니다.
우리가 채팅에 많이 사용하며, 메일을 보낼때도 많이 사용해 보셨을 기능입니다.

우리가 ctrl + c를 하게되면 클립보드라는 곳에 우리가 복사한 내용이 임시 저장되게 되는데,
이때 우리가 복사한 이미지는 이 클립보드의 item으로 저장이되게 되는 것입니다.

const handlePaste = async (event: React.ClipboardEvent<HTMLInputElement>) => {
        const items = event.clipboardData.items;
        for (let i = 0; i < items.length; i++) {
            const item = items[i];
            if (item.type.indexOf('image') !== -1) {
                const blob = item.getAsFile();
                if(!blob) return;
                const file = new File([blob], blob.name, { type: blob.type });
                setSelectImage(file);
                const reader = new FileReader();
                reader.readAsDataURL(file);
                reader.onloadend = () => {
                    if(reader.result && typeof reader.result === 'string') setSelectImagePath(reader.result);
                }
            }
        }
    }

위와 같이 clipboardData의 item들을 꺼내와 그중 image type인 데이터들만 뽑아내게 됩니다.
이때 저는 file형태로 관리를 하고 있어 file형태로 변형하여 state관리를 하였고, 미리보기 기능을 위해 FileReader를 사용하여 미리보기 데이터를 관리하였습니다.


코드: https://github.com/KrongDev/imageUploadUI

728x90

'Front > TypeScript' 카테고리의 다른 글

[Typescript] Null을 대체하는 값  (0) 2024.04.08
728x90

NATS란?

NATS는 클라우드 기반 애플리케이션, IoT 메시징 및 마이크로서비스 아키텍처를 위해 만들어진 가벼운 메시징 서비스입니다.

NATS Server는 Go언어로 구축되어있으나 NATS에서 제공하는 툴이나 라이브러리들을 많이 제공하고 있어 Cloud native Application, IoT Messaging, MSA등에서 많이 활용할 수 있습니다. 확인하러가기

NATS는 왜 사용하는 것인가??

  • 빠르다
  • 가볍다
  • 간편하다
  • 어디에든 배포가 가능하다
  • 최신 기술들 호환이 좋다

와 같은 여러가지 강점들을 가지고 있으며, 이를 토대로 kafka와 같은 Messaging 서비스들과 경쟁을 하고 있는 것 같습니다.

우선 NATS가 어떤 개념으로 작동하고, 어떤 기능들이 존재하는지에 대해 알아보도록 하겠습니다.


Subject-Based Messaging

NATS는 주제 기반 메시징을 지원하고 있습니다.
여기서 말하는 *주제는 무엇일까요?

Subject는 publisher와 subscriber가 서로를 알기 위한 문자열입니다.

이 Subject에는 몇가지 규칙이 따릅니다.

  • 권장 문자: a to b, A to Z 및 0 to 9(이름은 대소문자를 구분하며 공백을 포함할 수 없다.)
  • 특수문자: 마침표.(제목에서 토큰을 구분하는데 사용), *, > (각각이 와일드 카드로 사용됩니다)
  • 예약된 이름: $로 시작하는 이름은 시스템에 예약되어 있어 사용하면 안됩니다.

.은 이름의 그룹을 분간하기 위해 사용이 됩니다.

time.kr.soul
time.kr.busan

또한 *>는 이름이 매칭 부분을 조정할 수 있습니다.

*는 단일 토큰 매칭으로 *부분을 제외한 나머지 그룹군이 맞다면 매칭을 시켜줍니다.

>는 여러가지 토큰을 일치시켜줄 수 있는 것으로 이는 몇가지 조건을 맞춰야 사용할 수 있습니다.

  • 하나 이상의 토큰과 일치
  • 제목 끝에만 명시가능

위 조건을 만족할 경우 사용하여 토큰을 매칭시킬 수 있습니다.

와일드 카드는 혼합이 가능하며 *는 한 제목에 여러번 작성할 수 있습니다.
ex) *.*.east.>


Core NATS

NATS를 구성하는 모델들은 다음과 같습니다.

  • Publish-Subscribe
  • Request-Reply
  • Queue Groups

1. Publish-Subscribe

Publish와 Subscribe는 각각 메시지를 발행하는 송신자와 메시지를 수신하는 수신자로 봐도 좋을 것 같습니다.

Publish가 message를 발행할경우 Publisher와 매핑되는 토큰을 가진 Subscriber들이 message들을 읽어 처리하는 식입니다.

주고 받을 수 있는 Message는 아래의 필드들로 구성되어있습니다.

  • 과목
  • 패이로드(byte Array)
  • 헤더 필드의 수
  • reply(optional)

Message사이즈는 max_payload설정으로 설정이 가능하고, 기본적으로 1MB가 할당되어있습니다.
최대 사이즈 64MB까지 설정할 수 있지만 무작정 메모리 크기를 늘리기 보다는 꼭 필요한 만큼만 사이즈를 늘려 관리하는 것을 권장하고 있습니다.

2. Request-Reply


요청과 응답 부분입니다.
사용자 즉 Client가 서버에 요청을 보낼경우 서버는 보인이 처리해야하는 로직들을 처리 후 Message를 Publish합니다.

이때, 동기방식으로 Message처리 후 응답을 기다렸다 Client에게 응답을 하는 것이 아닌
서버에서 처리할 부분이 완료될경우 Message는 Publish한 후 Client에게 응답을 하고,
나머지 Message를 받아 처리하는 부분은 Background로 처리합니다.

3. Queue Groups


NATS의 로드밸런싱 기능입니다. 이는 분산 queue기능을 통해 처리하며 이것은 동일한 그룹 내에서 한번만 처리되도록 보장하기 위한 기능입니다.

Queue Groups는 다음과 같은 특징을 가지고 있습니다.

  1. Load Balancing
  2. Message Delivery Guaratees
  3. Scalability
  4. Fallover

위와 같은 특징들은 모두 Message를 동일한 Queue Group내에서 한번만 처리되도록 하기 위해 분산 처리, 전달 보장, 장애 복구의 기능을 통해 메세지가 처리되도록 보장하고 있습니다. 또한 위와 같이 분산처리를 할 경우 K8s 환경에서 개발을 할경우 Pod를 얼마나 늘리든지 상관없이 얼마든지 확장 강능하다는 강점을 가지고 있습니다.

JetStream

NATS server에는 NATS의 기능보다 확장된 기능들을 제공하는 built-in distributed persistence system이 존재하는데 그것이 JetStream입니다.

JetStream에서 제공하는 기능은 다음과 같습니다.

  • Streaming
  • Replay policies
  • Retention policies and limits
  • Limits
  • Retention policy
  • Subhect mapping transformations
  • Persistent and Consistent distributed storage
  • Stream replication factor
  • Mirroring and Sourcing between streams
  • De-coupled flow control
  • Exactly once semantics
  • Consumers
  • Fast push consumers
  • Horizontaliy scalable pull consumers with batching
  • Cosumer acknowledgments
  • Key Value Store

이러한 기능들은 Option이므로 사용하실 때 필요한 기능들을 찾아 적용하시는 것이 좋을 것 입니다.

또한 NATS는 가볍고 빨라야하기 때문에 Event의 Message 즉 Payload가 적정선 이상으로 커지는 것을 경계하는 것으로 보입니다. Message를 설계를 할 때 Message의 크기를 8MB로 제한하고 설계한다면 NATS의 성능을 제대로 활용할 것 같습니다.


참고자료: https://docs.nats.io

728x90
728x90

우리가 개발을 하다보면 Redis라는 용어를 한번쯤 들어보았을 것이고, RedisTemplate라는 것을 한번은 코드상에서 봤을 수 있습니다.

개발을 하면서 데이터를 저장할 때 쓴다고 한 Mysql, Mongodb등 기존 데이터 베이스를 두고 왜 Redis라는 Database를 또 사용하는지, 그리고 어떻게 사용하는지에 대해 다뤄보겠습니다.

Redis란?

Redis는 REmote Dictionary Server의 약자이며, Mysql과 같이 서버와는 별개로 저장이 되지 않고 메모리상에 저장되는 In memory구조로 구성되어있습니다.

데이터는 Key Value로 관리가 되고 있으며, 다양한 구조 집합을 제공함으로 다양한 구조를 Value로 저장할 수 있으며 최대 512MB의 데이터가 저장가능합니다.

Key로 데이터를 관리하기때문에 조회에 있어 가장 빠른 성능을 자랑하며, 빠른 조회 성능을 요구하는 캐싱, 세션 관리 등에 사용을 하고 있습니다.

Redis는 In memory 구조로 key value형태로 데이터를 저장 하고 있으며, key로 데이터를 관리하기때문에 조회에 있어 빠른 성능을 자랑하며, 현재 가장 인기 있는 key value store입니다.

Key Value로 처리하면서 Redis는 Row로 저장되는 것 보다는 Column을 저장한다는 것으로 보이고, 이때문에 많은 양의 데이터를 저장하고 지속적으로 관리하는 기존 데이터베이스와 사용용도는 다르다고 필자는 생각합니다.

또한 InMemory구조로인한 데이터를 지속적으로 백업해주어야하는 문제가 있으며, 이를 해결하여 사용하기보다는 기존의 다른 데이터베이스와 함께 사용하면서 용도에 맞춰 사용하는 것이 가장 현명한거 같다 생각합니다.

Redis는 요청에 의했을 때는 Single Thread 개념을 가져오지만, background에서는 Multi Thread를 지원하는 만큼 데이터를 조회하는데에 있어 빠른 성능을 강점으로 가져가는 것 같습니다.

이처럼 Redis는 java의 Map과 같이 Key Value로 데이터를 저장하며, inmemory구조지만 Redis를 사용하는 상황은 분산 시스템에 있습니다. 분산시스템에서 동일한 key value의 저장소를 공유해야만 하는 상황이라면 Redis를 사용하여 해당 Issue를 해결하는 방법도 좋은 방법이 될 수 있습니다.


사용법

설치

우선 필자는 Database관리를 Docker로 하고 있기 때문에 Container로 만들어 관리를 하도록 하겠습니다.

1. Redis Image Pull

docker pull redis

Docker에서 지원해주는 Redis Image를 다운받아줍니다.

2. Redis Container run

docker run --name test-redis -p 6379:6379 redis

redis이미지를 토대로 container를 생성하여 실행하여줍니다.
port는 redis기본 port인 6379로 설정하였습니다.

3. redis접속

docker exec -it test-redis /bin/bash

위 명령어를 통해 실행중인 container에 접속해주도록 합니다.

redis-cli

위 명령어 입력을 완료하면 container로 실행중인 redis안에 접속할 수 있습니다.

4. CRD

set {key} {value}
get {key}
del {key}

위 명령어와 같이 set을 통해 데이터를 등록할 수 있습니다.
key는 식별자 역할을 함으로써 수정할 때에도 동일하게 set명령어를 사용할 경우 해당 keyvalue를 변경할 수 있습니다.

이처럼 Redis자체를 사용하는 것은 쉽게 사용할 수 있습니다.
그럼 이제 서버에서 어떻게 사용하는지를 알아보겠습니다.

JAVA에서 Redis사용법

spring:
  data:
    redis:
      host: localhost
      port: 6379

Application.yml설정입니다.
Redis기본 port가 6379이기 때문에 따로 port가 겹치지 않아 6379Port로 설정하여 사용하였습니다.

@Configuration
public class RedisConfig {
    @Value("${spring.data.redis.host}")
    private String redisHost;
    @Value("${spring.data.redis.port}")
    private int redisPort;

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(redisHost, redisPort);
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate() {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

RedisConfig설정입니다.
연결을 위해 redisHost와 redisPort값을 받아 Redis저장소에 연결 즉 connect를 하고, 이후 RedisTemplate를 정의함으로써 보다 편하게 사용할 수 있게 정의하였습니다.

@Repository
@Transactional
@RequiredArgsConstructor
public class TestStore {
    //
    private final RedisTemplate<String, String> redisTemplate;

    public void create(String key, String data) {
        ValueOperations<String, String> opreation = redisTemplate.opsForValue();
        opreation.set(key, data);
    }

    public void update(String key, String data) {
        ValueOperations<String, String> opreation = redisTemplate.opsForValue();
        opreation.set(key, data);
    }

    public String load(String key) {
        ValueOperations<String, String> opreation = redisTemplate.opsForValue();
        return opreation.get(key);
    }

    public void remove(String key) {
        redisTemplate.delete(key);
    }
}

TestStore입니다.
RedisTemplate를 통해 데이터를 CRUD를 하는 작업을 담당하게 구현하였습니다.


참고자료: https://redis.io/docs/

728x90

+ Recent posts