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
728x90

Code::Blocks를 설치 하였으면 우리의 목적은 C or C++를 공부하기 위해서니 해당 언어를 사용할 수 있는 작업 공간이 필요하다.

 

이를 위해 많은 용어가 떠다니는 Code::Blocks에서 어떻게 프로젝트를 만들 수 있는지 알아보자.

 

우선 Code::Blocks에 들어오게 될 경우 아래와 같은 화면이 나올것이다.

이때 저 가장 클릭하고 싶게 생긴 Create a new project버튼을 클릭해 주도록 하자.

 

이후 어떤 프로젝트를 생성할 것인지와 관련하여 선택하라는 Modal창이 뜰 것이다.

 

잘 사용하는 분들은 원하는 것을 선택하면 될 것이고, 필자와 같이 C++언어 뉴비들은 정석 코스인 Console application을 선택하여 project를 생성하도록한다.

 

그러고 나면 해당 project를 진짜 만들거냐 물어보는데 쿨하게 스킵하도록 하자

다음 단계로 사용할 언어를 고르라고 나올 것이다.

C와 C++은 큰 차이가 있으니 해당 부분은 확실히 구분을 하고 난 이후 언어를 선택하도록 하자.

 

언어까지 다 고르셨으면 다음 step으로 가장 중요한 Project의 이름을 설정하는 step이 나올 것이다.

Project의 이름은 해당 코드의 얼굴이라 할 정도로 중요하다 생각한다.

보통 코드를 보고 어떤 기능을 하는지에 대해 고민하기 전에 Project의 이름을 보고 "어떤 기능을 할 것이다!"라고 짐작 할 수 있기 때문에 필자처럼 test목적이 아닐 경우 무작정 test라 이름을 정의 할 경우 다음 개발자로부터 살인예고장을 받을 수 있으니 조심 또 조심하길 바란다.

 

※ 주의 사항 ※
아마 필자처럼 Folder to create project in 을 지정안하고 프로젝트를 생성 할 경우 Code::blocks가 죽고 process만 살아 재실행이 안되는 경우가 생길 수 있다.

그럴때는 Command를 관리자 권한으로 실행 이후 "tasklist"로 process확인한 이후 "taskkill /f /im codeblocks.exe" 를 사용하여 process를 죽이면 정상적으로 재실행 할 수 있다.

결론 : project어디에 생성할지 지정해 주자.. (어디 만들지 지정을 안했을 경우 경고를 하나 띄워주면 얼마나 좋을까하는 아쉬움이 있다...)

 

 마지막 step인거 같다.

대강 읽어보니 해당 코드를 검사해줄 compiler를 선택하라는 것 같은데 이는 지금 알아보기엔 어서 코드를 짜고 싶기 때문에 우선 skip하도록 하겠다.

 

봐보니 기본적으로 GCC Compiler를 mapping해주는 것 같기 때문에 무지성 finish버튼을 클릭해 보았다.

 

이로써 코딩할 준비가 된거 같다! 

다음편부터 C or C++에 관해 다루도록 하겠다.

 

처음 시작에 애먹는 분들에게 도움이 되었기를 바라며...

728x90

'C언어 개발 > C++독학하기' 카테고리의 다른 글

[C언어 독학하기] Code::Blocks 설치하기  (2) 2022.10.09
728x90

좋은 아침입니다.

 

이번 post내용은 제목에서 보이듯이 ApplicationContext에서 지원해 주는 getBean이란 메소드를 사용하여 Bean들을 다루는 것에 대해 포스팅 할 예정이다.

 

우리가 업무를 진행할 때 가끔(?) 이런 요구 사항을 받을 수 있다.

 

1. 작업 내용이 몇개가 추가 될 지 모른다.

2. 업무의 종류는 같지만 하는 일은 다를 수도 있다.

 

즉 몇개의 항목이 있는지도 모르고, 추가되는 항목이 있을 때 그 항목이 하는 일도 다를 수 있다는 뜻이다...

 

만약 저런 요구조건이 들어오게 된다면 우리는 if 혹은 switch를 사용하여 코드를 더럽힐 수도 있다.( 필자가 그랬다... )

 

하지만 세상은 바보가 아니고 그를 좀더 합리적으로 해결 할 수 있는 방법은 이미 알게모르게 제공을 해주고 있다.

 

바로 추상화 즉 Interface를 사용하는 것이다.

 

읭...? 갑자기 Interface?? 라고 생각할 수도 있다.

하지만 그렇게 생각한다면 Interface 무엇인가 부터 고민을 해봐야한다 생각한다.

 

Interface가 무엇인가?? 바로 어떤 행위를 규칙으로 정해두고 구현체들에게 해당 행위를 정의하도록 하는 것이 Interface의 역할이다 라고 볼 수 있다고 생각한다.

 

그러니 위처럼 끝이 확실하지 않고 확장을 할 가능성이 있는 요구조건이 들어오면 행위들을 Interface로 설계를 하고 추가 생성을 해야한다면 해당 업무를 담당하는 Class를 만들어 구현하기만 하면 되어 기존 코드에 영향력을 끼치지 않을 수 있다는 장점도 추가로 가져갈 수 있다.

 

이제 Interface를 왜 사용하는지도 알았으니 본문 Bean들을 불러오는 것에 대해 말해보도록 하겠다.

 

고맙게도 Spring에서 지원하는 ApplicationContext에는 Class type을 가지고 있는 Bean들의 이름을 가져올 수 있는 메소드를 지원해준다.

 

우선 사용할 Interface를 만들어준다.

public interface WorkGroup {
    void work();
}

 

이후 해당 Interface를 구현한 Class들을 만들어주면 준비는 끝이다.

그럼 이제 Bean들의 이름을 통해 Bean들을 가져오는 코드를 만들면 된다.

@Component
public class BeanHandler {
    //
    private final List<WorkGroup> works;

    public BeanHandler(ApplicationContext applicationContext) {
        //
        this.works = new ArrayList<>();

        String[] beanNames = applicationContext.getBeanNamesForType(WorkGroup.class);
        for (String beanName: beanNames) {
            this.works.add((WorkGroup) applicationContext.getBean(beanName));
        }
    }
}

getBeanNamesForType()메소드를 사용할 경우 Interface의 class type을 명시해주면 해당 interface를 구현한 구현체들의 Bean이름을 가져올 수 있다.

 

이름을 가져왔다면 applicationContext.getBean() 메소드를 사용하여 해당 이름을 가진 Bean을 가져와 주면 된다.

 

이제 모든 준비가 끝났다 안에서 각자 다른 업무를 진행하는 Class들이지만 우리는 해당 업무를 work()라 정의하여 interface를 만들었고 각각 Class들은 interface를 구현한 구현체다 보니 어떤 Class든 work()메소드를 호출하면 각자 필요에 맞는 업무를 진행할 것이다.

@Component
public class BeanHandler {
    //
    private final List<WorkGroup> works;

    public BeanHandler(ApplicationContext applicationContext) {
        //
        this.works = new ArrayList<>();

        String[] beanNames = applicationContext.getBeanNamesForType(WorkGroup.class);
        for (String beanName: beanNames) {
            this.works.add((WorkGroup) applicationContext.getBean(beanName));
        }
    }

    public void process() {
        //
        log.info("Process Start");
        for (WorkGroup workGroup: works) {
            workGroup.work();
        }
        log.info("Process End");
    }
}

이로써 각자 업무 내용이 다르더라도 process가 시작되면 각각의 그룹들이 일을 하는 것을 지금은 log를 통해 확인해 볼 수 있다.


요구사항은 우리 개발자들이 원하듯이 딱 정해져서 오는 것 보다 유동적인 변화를 가진 요구사항이 오는 경우가 제 짧은 개발 인생에는 더 많은거 같다.

 

그랬을 때 당황하지 않고 이전의 해결방법들을 참고하여 더 좋은 해결방안을 내는게 좋을거 같다.

 

이글 이 도움이 되길 바라며 모두 화이팅 하길 바란다. 이상!

728x90

'Server' 카테고리의 다른 글

REST API란?  (0) 2024.04.14
[Docker] 도커로 데이터 베이스 편하게 사용하자~  (1) 2024.04.08
[Docker] Docker 설치하기  (0) 2024.04.08
[Docker] Docker란 무엇인가?  (2) 2024.04.08
NATS란?  (0) 2024.04.08
728x90

최근 관심을 가지게 된 C언어를 독학하기로 했다.

우선 기존 사용하던 JAVA나 javaScript와 다른 언어인 만큼 초심으로 돌아가 차근차근 시작해 보기로 하였다.

모든 개발의 첫번 째 환경 구축 하는 것부터 시작하겠다.

개발 환경 구축하기

Code::Blocks 설치하기

구글링을 통해 C언어를 개발할 수 있는 개발툴을 찾아보았는데,

고심한 결과 Code::Blocks를 설치해보기로 하였다.(특별한 이유는 없다...)

 

https://www.codeblocks.org

 

Code::Blocks

The IDE with all the features you need, having a consistent look, feel and operation across platforms.

www.codeblocks.org

우선 해당 사이트로 들어가게 되면 Downlods가 있는데 해당 링크로 들어가도록한다.

 

이후 위와 같이 Download the binary release를 클릭하여 Download하러 들어간다.

 

들어가면 Binary release가 나오게 되는데 사용하고 있는 운영체제에 맞는 곳으로 들어가면 된다.

 

여러가지 설치 파일들이 존재하는데 필자의 경우 codeblocks-20.03mingw-setup.exe파일을 설치하였다.

해당 파일을 설치한 이유는 mingw-setup으로 설치를 안할 경우 직접 컴파일러를 설치하고 설정해야 하는것으로 알고있어서 해당 파일로 설치를 하였다.(구글이 알려주더라고..)

 

이후 진행사항은 아무런 할것없이 그냥 next로 넘겨 다운을 완료하면된다.

 

이상으로 설치하는 법을 작성하였고 다음편에서 마저 독학해보도록하겠다...

화이팅...

728x90

'C언어 개발 > C++독학하기' 카테고리의 다른 글

[C언어 독학하기] Code::Blocks 시작하기  (0) 2023.05.02

+ Recent posts