// 에드센스

https://media.fastcampus.co.kr/insight/why-blockchain-is-hard/

블록체인에 대해 전반적인 그림을 그리기 위해 조사하며 얻은 얕은 지식입니다.


1. 왜 검증이 필요한가?

블록체인에서 블록은 곧 데이터베이스다.

근데 이 데이터베이스는 모든 사람이 접근하여 작성할 수 있다.

이 데이터베이스를 거래장부라고 할 수 있다.

거래장부에 모든 사람들이 접근하여 거래를 기록할 수 있다면 어떻게 될까?

 

만약

"A가 B에게 100만원을 줬다."

라는 진실된 거래가 있다.

하지만 B가 돈을 갚기 싫어서 다음과 같은 가짜 거래 내역을 썼다.

"B가 A에게 100만원을 줬다"

그러면 실제로 B가 돈을 갚지 않았지만 A와 B사이에는 더 이상 빚진 돈이 없게된다.

 

이런 일을 방지하기 위해 거래내역을 위조할 수 없도록 검증이 필요하다.

 

 

 

 


2. 검증 방법

현실세계에서는 자필 서명이나 도장을 찍는다.

사람이라면 타인의 자필 서명을 완벽하게 흉내낼 수 없다는 전제,

도장은 자신이 소중하게 관리를 한다는 전제,

가 있기 때문이고 이는 잘 작동된다.

 

하지만 모든것이 ctrl+c / ctrl+v로 복제가 되는 인터넷에서는 어떻게 할까?

 

 

 

1. 트랜잭션이란?

데이터베이스에서 상호작용 및 수행의 논리적 단위를 뜻하는 단어이다.

블록이 곧 데이터베이스이니 이렇게 표현한다.

 

 

 

2. 비대칭 암호화 방식

비트코인을 포함한 많은 암호화폐들이 이 방식을 사용한다.

혹은 공개키 알고리즘이라고도 부른다.

 

클라이언트에서 거래를 하면 거래내역이 생성된다.

클라이언트는 이 거래내역을 노드에게 전송하여 검증을 맡긴다.

노드가 이제 검증을 수행하게되는데,

이를 위해서는 거래내역에 노드에게 전송될 때 "전자서명"이 포함되어야 한다.

비대칭 암호화 방식을 통해 전자서명을 생성한다.

우선 다음의 배경지식이 필요하다.

  • 모든 클라이언트들은 자신의 [공개키]와 [개인키]가 있다.
  • [공개키]는 모두에게 노출되어도 괜찮은 키이다.
  • [개인키]는 오직 자신만 알고있어야 하는 키이다.
  • [개인키]로 암호화한 데이터(거래내역)은 [공개키]로만 해독할 수 있다.

[개인키]를 [비밀키], [비공개키]라고도 하는데 나는 그냥 [개인키]라고 하겠다.

 

[개인키], [비밀키], [주소]는 다음과 같은 관계를 가진다.

https://www.google.co.kr/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0ahUKEwi567Lu-ojVAhXDHJQKHZWcC-cQFggmMAA&url=https%3A%2F%2Fdocs.google.com%2Fa%2Fieee.org%2Fuc%3Fid%3D0Byw4AEomZK2edlQwel95S1J0VTg%26export%3Ddownload&usg=AFQjCNHfjSAx7d59S3UDVHjxBQcl3OmkWw

[개인키]를 타원곡선 곱셈함수로 돌리면 [공개키]가 나오고,

[공개키]를 해시함수로 돌리면 [주소]가 나온다.

이 흐름은 그림에서처럼 일방향으로만 진행할 수 있다.

[개인키]로 [공개키]를 알 수는 있지만,

[공개키]로 [개인키]를 알아낼 수는 없다는 것이다.

 

 

 

3. 검증 과정

다른 블로그의 글에는 막 그림도 있고 그러던데, 우리는 그냥 눈을 감고 생각해 보자. 오히려 이해가 잘 될지도?

 

  1. 클라이언트 A는 자신의 거래 내역이 있다.
  2. A는 자신의 거래 내역을 자신의 [개인키]로 암호화한다.
  3. 그 후 A는 노드에게 [개인키로 암호화한 거래내역] + [원본 거래내역] + [A의 공개키]를 세트로 보낸다.
  4. 노드는 저거 3종 세트를 받아서 검증을 수행한다.
  5. 우선, [A의 공개키]로 [개인키로 암호화한 거래내역]을 해독하고 그걸 [원본 거래내역]과 비교한다.
  6. 비교 결과 동일하면 이 거래내역은 A가 생성한 올바른 거래임을 인정한다.
  7. 이후 검증된 거래내역은 해당 노드 근처의 다른 노드들로 전파된다.

5번 과정이 핵심이다.

 

참고로 거래내역을 바로 [개인키]로 암호화하는 것이 아니라

[개인키]를 해시함수로 돌려서 해시값을 뽑아내고 그 해시값을 [개인키로] 암호화하는 것이다.

이렇게 하면 거래내역이 아무리 길어도 고정된 길이의 해시값이 나오기에 송수신시 유리하다.

 

 

 

 


3. 검증자가 조작하면?

A와 검증노드가 합심해서 조작하면 어떻게 될까?

A가 가짜 거래내역을 노드에게 검증신청을 하고,

이 노드가 가짜 거래내역임에도 불구하고 검증완료 처리를 한다면?

 

이를 막기위해 신뢰할 수 있는 노드(검증자)만 검증을 진행할 수 있도록 제한한다.

신뢰할 수 있는 노드를 가리기 위해

 

  1. 검증에 참여하기 위해서는 비용이 발생하도록 만든다.
  2. 검증이 완료되면 네트워크에서 보상을 지급한다.

이런 안전장치를 마련해 두었다.

일종의 입장료를 내야만 검증에 참여할 수 있기에 나쁜 의도를 가진 검증 참여자를 걸러낼 수 있다.

또 보상으로 암호화폐를 받기에 이 화폐의 가치가 올라가기 위해서는 정직한 검증이 이어져야 한다. 

즉, 검증을 정직하게 해야만 하는 이유가 생기는 것이다.

 

 

 

 


4. 헷갈렸던 부분

공개키와 개인키 중에 어떤 것으로 암호화를 하고 복호화를 하는지 헷갈렸다.

내가 지금까지 알아온 방식은

 

  1. A와 B가 있고, A -> B로 데이터를 보낸다면
  2. A는 [B의 공개키]로 보낼 데이터를 암호화해서 보내면
  3. B는 자신만 가지고있는 [B의 개인키]로 복호화해서 보는 것

(이 비유에서 A는 트랜잭션의 생성자가 되겠고, B는 검증노드가 되겠다.)

 

 

 

이런 흐름이었는데 블록체인의 전자서명도 같겠거니 했지만,

[개인키]로 암호화해서 [공개키]로 복호화한다는 점이 반대였다.

누구나 가질 수 있는 [공개키]로 복호화하게 되면 데이터를 누구나 들여다 볼 수 있지 않은가?

라는 생각에 빠져 혼란이 왔었는데,

 

 

생각해보니 데이터를 비밀스럽게 보내기 위한 비대칭키 암호화 방식이 아니라,

데이터를 생성(전송)한 사람의 본인확인을 위한 절차였기에 개인키, 공개키를 반대로 쓰는 것 이었다.

 

 

굳이 검증노드가 생성된 트랜잭션을 비밀스럽게 확인 할 필요는 없고,

오히려 모든 검증노드가 이런 트랜잭션을 까봐서 올바른 트랜잭션인지 확인하기 위해서는 공개키로 복호화를 해야만 하는 것이었다.

 

A의 공개키로 복호화가 된다면?

그 암호는 A의 비밀키로 암호화 했다는 의미 

왜?

비밀키 -[해싱]-> 공개키 -[해싱]-> 지갑주소

이기에 공개키로 해독이 되는 암호는 그 공개키에 해당하는 비밀키로 암호화음이 분명하다.

 

 

[개인키] <-> [공개키] 양쪽으로 암/복호화가 된다는 성질을 이렇게도 활용하는구나 싶어서 소소한 깨달음이었다.

뭔가 적어놓고 보니 당연한 사실이긴한데,

처음 전자서명 암호화를 접할 당시에는 헷갈렸다ㅠㅠ

 

 

 

 


아무튼,

장황하게 썼지만 생각보다 간단하다.

이렇게 많은 노드들로 전파된 거래내역을 채굴노드가 모아서 블록을 생성한다.

이 과정은 다음 글에서 알아보겠다.

 

 

 

 

 

참고:

더보기

 

 


1. 언제 사용할까

develop 브랜치에서 분기한 feat1 브랜치가 있다고 하자.

feat1 브랜치에서 작업을 하며 여러가지 변경사항들이 생겨나고있다.

이때 develop 브랜치나 다른 브랜치로 checkout 해야할 경우가 종종 생긴다.

 

이때 그냥 checkout을 시도하면 git은 commit을 먼저 하라는 경고를 뱉는다.

하지만 난 아직 feat1 브랜치에서의 작업이 끝나지 않아서 커밋하고 싶지 않다면?

 

이때 git stash 명령어를 사용할 수 있다.

 

 

 

 


2. 사용법

feat1 브랜치에서 

git stash

를 쳐주면 feat1에 내가 싸놓은 변경사항들이 스택으로 잠깐 이동된다.

즉 feat1과 내가 분기해서 가져온 로컬 develop 브랜치간의 차이가 없어지고 

다른 브랜치로 이동할 수 있게된다.

 

 

 

다른 브랜치에서 작업을 끝내고 다시 feat1로 돌아와서 내가 옮겨 두었던 변경사항들을 불러오고 싶다면

git stash apply

를 해주면 된다.

 

 

 

단, 이때 스택에 여러개의 stash된 내용들이 쌓여있다면 가장 최근에 stash된 내용이 꺼내와지며(이 경우 stash@{0})

 

 

꺼내와졌다고 자동으로 pop되지 않기에

git stash drop

을 통해 꺼내온 stash 내용을 지워주어야 한다.

 

 

 

 

참고:

https://media.fastcampus.co.kr/insight/why-blockchain-is-hard/

블록체인에 대해 전반적인 그림을 그리기 위해 조사하며 얻은 얕은 지식입니다.


 

1. 어디에 저장?

노드에 저장된다

블록체인을 설명할때 주로 블록은 데이터베이스, 체인은 체인 이라는 표현을 한다. 

블록에 정보를 저장하고 체인형태로 쭉 이은 구조이기 때문인데,

이러한 탈중앙화 구조로 인해 해킹에 안전하다고 한다.

 

그런데 문뜩 궁금해졌다. 거래내역과 같은 정보들은 블록에 저장된다고 하는데,

그럼 이 블록은 실제로 어디에 존재하는 것일까?

무엇이 됐든 물리적인 저장 공간은 있어야 기록이 계속 유지되지 않는가?

어딘가의 서버에 존재하면 그것은 중앙화된 구조기에 말이 안되지 않는가?

 

 

 

 


2. 블록체인 노드

블록들은 노드에 저장된다.

노드라는 단어를 많이 들었지만 정확히는 잘 몰랐다. 

우선 암호화폐 시장은 3종류의 사람들로 구성된다.

  1. 채굴자 (컴퓨터로 암호를 풀고 블록을 생성할 권한을 얻음)
  2. 소비자 (생성된 코인을 가지고 거래를 함)
  3. 네트워크 참여자 (거래가 안전한지 검토하고 승인하는 관리자)

이때 네트워크 참여자들이 사용하는 기계를 노드라고 한다.

노드는 새로운 블록이 생성되면 그 블록이 안전한 거래인지 검토하고 승인하는 역할이다.

데이터를 다운로드할 수 있는 모든 종류의 전자기기는 노드가 될 수 있다.

즉, 모든 스마트폰이나 데스크탑 PC가 노드가 될 수 있다. 

 

노드가 되려면 블록체인 네트워크에 접속하면 된다.

블록체인 네트워크에 접속하려면 데이터(블록)를 다운받거나 지갑을 생성하는 등의 행위를 의미한다.

이런 노드는 전 세계에 무수히 많이 있기에 탈중앙화었다고 말할 수 있고, 이를 해킹하기 위해선 전세계의 스마트폰과 데스크탑을 해킹해야 한다는 말이 된다.

 

검증의 원리와 과정은 다음 포스팅으로 알아보도록 하자.

 

 

 

 


3. 노드의 종류

노드는 풀노드라이트노드로 구분할 수 있다.

더 많은 종류의 노드가 있긴하지만 일단 대표적인 종류부터 알아보자.

 

 

1. 풀노드

풀노드는 모든 기능을 다 가지고 있는 노드이다.

이때 모든 기능이란

  • 지갑
  • 채굴
  • 데이터베이스
  • 라우팅

을 의미한다.

풀노드는 최초의 블록부터 최신의 블록까지 모든 블록을 가지고있다.

따라서 독자적으로 어떤 거래에 대한 검증을 할 수 있다.

모든 블록을 저장하고 있어야하기에 저장공간이 많이 필요하다.

 

비트코인의 경우 최초블록(제네시스블록)부터 현재의 블록까지 모든 정보를 저장하기 위해서는 200GB 이상의 용량이 필요하다고 한다. 

 

이곳에서 소프트웨어를 다운로드받으면 풀노드가 될 수 있다.

https://bitcoin.org/ko/download

 

다운로드 - Bitcoin

Bitcoin.org is a community funded project, donations are appreciated and used to improve the website. Make a donation

bitcoin.org

 

2. 라이트노드

라이트노드는 블록체인의 모든 블록을 가지고있지는 않다.

따라서 풀노드보다 요구되는 저장용량이 적다.

헤더정보만 가지고있는 일종의 요약본이다.

라이트노드도 거래는 가능하다. 다만 거래에 대한 검증은 불가능하다.

검증을 하기위해 풀노드에 검증된 거래인지 머클트리를 통해 확인하는 요청이 필요하다.

이를 SPV(Simple Payment Verification)라고 한다.

 

 

 

 

참고:

더보기

 

아침에 노트북을 켜서 보니 갑자기 안뜨던 에러가 떴다.

 

npm install --g eslint

 

를 해주고 인텔리제이를 재실행했더니 해결됐다.

 

(권한문제가 발생하면 sudo를 사용하자)

 

이번에 새로운 CICD를 구축할 일이 생겼는데 어떤 툴을 사용할지 조사하게 됐다.

조사하는겸 기록해보자

보고계십니까 엘리님...


1. CI/CD란?

 

"개발부터 배포까지 모든 단계를 자동화 하는 것"

 

 

CI

CI는 Continuous Integration의 약자로 지속적 통합이라는 의미이다.
애플리케이션의 새로운 코드들이 자동으로 빌드 및 테스트 되어 레포지토리에 통합되는 것을 의미한다.

 

CI의 포인트는

  • 개발자들은 최대한 작은 단위로 만들어서 개발해가며 빈번하게 merge해야한다.
  • 애플리케이션들의 빌드, 테스트, 병합하는 과정을 주기적으로, 자동화시켜야 한다.

 

이런 포인트를 따라가면

  • 병합시 충돌을 예방할 수 있고,
  • 코드의 결함이나 문제점을 빠르게 발견할 수 있다.

 

또 문제점을 빠르게 발견할 뿐 아니라 빠르게 해결할 수 있다.

왜? 작은 단위로 빈번하게 merge하기에 문제 발생 범위가 작기 때문이다(빈번하게 merge해도 빌드, 테스트가 자동이라 간편)

결과적으로 코드의 품질이 올라간다.

 

 

 

CD

CD는 Continuous Delivery/Deployment의 약자로 지속적 제공/배포라는 의미이다.
CI를 통해서 빌드, 테스트가 완료되어 배포될 준비가 끝난 애플리케이션을 개발자가 수동으로, 혹은 자동으로 배포를 진행하는 것.

(수동일때를 Continuous Delivery, 자동일때를 Continuous Deployment라고 한다. 최종단계의 자동화 여부로 나눈다고 하는듯?)

 

 

 

CI/CD 파이프라인

5줄 요약하자면,

 

  1. 개발자가 작은 단위로 코드를 짜고 메인 리포지토리에 merge를 하면,
  2. 자동으로 빌드되고,
  3. 자동으로 테스트되어,
  4. 릴리즈되고(배포준비완료),
  5. 배포된다.

 

 

 

 

 


2. CI/CD를 위한 도구

정말 많다.

https://ichi.pro/ko/hyeonjae-sayong-ganeunghan-choegoui-ci-cd-dogu-27gaji-194611649728144

 

현재 사용 가능한 최고의 CI/CD 도구 27가지

CI(지속적 통합) 및 CD(지속적 배포)(또는 CI/CD)는 소프트웨어 개발 및 DevOps 테스트의 필수적인 부분이 되었습니다. 개발자가 코드를 지속적으로 배포할 수 있도록 필요한 기능을 제공합니다.

ichi.pro

 

이 중에서 가장 대중적인 후보 3가지를 골라서 장단점을 조사해보았다.

 

 

Jenkins

  • 무료(단, 별도의 서버 필요)
  • 다양한 플러그인, IDE를 지원
  • 많은 사용자와 많은 문서
  • 규모가 작은 프로젝트의 경우 설정하는데 리소스 낭비가 발생할 수 있다.
  • 지라와 연동이 불편하거나 완벽하지 않을 수 있다.

무료라는 점과 많은 사용자를 가졌다는 점이 매력적이다.

특히 많은 문서와 선례들을 통해서 거의 모든 문제상황에 대처할 수 있을 것이다.

 

다만 하나같이 설정과 운영하는 측면에서 불편하다는 평이 많다.

손도 많이 가고 특히 별도의 서버를 준비해 거기에 설치하여 운영하는 방식이라 무료지만 실제론 무료가 아닌? 그런 느낌

(주로 t2.medium을 권하더라)

규모가 작은 프로젝트에서는 그렇게 추천하진 않는다.

 

 

 

Travis

  • 깃허브와 연동
  • 빌드 과정을 한 눈에 이해하기 쉽다.
  • 초기 설정이 젠킨스에 비해 간편(YML 파일을 통한 설정)
  • 별도의 서버 필요 없다. travis에서 알아서 VM으로 호스팅 해준다.
  • 기업용의 경우 다소 비싸다(월 129달러)
  • 로컬에서 CI환경과 동일한 빌드환경을 제공하지 않는다.
  • 젠킨스에 비해 플러그인이 다양하지 않다.
  • .travis.yml 파일을 수정하고 테스트하려면 git push를 반복해야한다.

 

 

Github Actions

  • 복잡한 과정없이 바로 깃허브에서 사용할 수 있다.
  • 빌드 과정을 눈으로 확인하기 쉽다.
  • 깃허브의 모든 이벤트에 대한 작업을 제공하고 다양한 언어와 프레임워크를 지원한다.
  • 젠킨스보다 빠르다.
  • public은 무료, private 저장소의 경우 매월 3000분 무료
  • 문서가 비교적 부족하다.
  • UI에서 개별 워크플로우 실행을 삭제할 수 없다.
  • 워크플로우에서 단일 작업만 다시 실행할 수 없다.

요즘 대세는 깃헙액션인것 같다.

빠르고, 서드파티가 필요없기 때문에.

다만 얘도 설정이 다소 복잡하다는 평이 있지만 젠킨스만큼은 아니고 그만큼 정교한 작업이 가능하다는 뜻이기도 하다.

(직접 써봐야 알겠지만ㅠ)

 

 

다음은 뱅크샐러드에서 Travis를 사용하다가 Github Actions로 전환 후 개선된 점이다.

 

 

 

 

참고:

더보기

또또 너무 오랜만에 글을 쓴다. 퇴근하면 왤케 피곤한걸까.

기존에 사용하던 stoplight에서 swagger로 전환하는 작업을 했다.

왜? 코드 안에서 바로 수정할 수 있기에 더 간편해서.

근데 한땀한땀 핸드메이드로 스웨거 주석을 작성하다가 스웨거 자동생성 라이브러리를 발견했고 그것을 사용해 보았다.


1. swagger란?

  • Open Api Specification(OAS)를 위한 프레임워크이다.
  • API들이 가지고 있는 스펙(spec)을 명세, 관리할 수 있는 프로젝트/문서
  • API 사용 방법을 사용자에게 알려주는 문서
  • express에서는 주석형태의 yaml형식으로 swagger 문서를 정의할 수 있다.
  • URL에 /api-docs으로(내가 지정한) 접근하면 swagger가 만들어주는 페이지에 접근할 수 있다.

 

 

 


2. swagger 자동 생성 라이브러리 swagger-autogen

https://github.com/davibaltar/swagger-autogen

 

GitHub - davibaltar/swagger-autogen: This module performs the automatic construction of the Swagger documentation. The module ca

This module performs the automatic construction of the Swagger documentation. The module can identify the endpoints and automatically capture methods such as to get, post, put, and so on. The modul...

github.com

 

일반적인 express swagger 적용은 이미 다른 블로그에도 친절하게 잘 적혀있다.

하지만 api가 하나 추가될때마다 한땀 한땀 주석을 작성(혹은 복붙)하기엔 너무 귀찮을 것 같았다.

 

그래서 찾아본 결과 router가 위치한 파일의 경로를 알려주면 자동으로 해당 router를 인식하고,

그 밑에 딸린 router들도 인식하여 api 문서를 위한 json파일을 자동 생성해주는 라이브러리를 찾았다.

 

 

npm start를 한 후의 동작을 보자면

  1. package.json의 prestart 스크립트를 통해서 swagger.js를 실행한다. 이 파일은 지정된 경로에 존재하는 파일들에서 라우터와 swagger 주석들을 읽는다.
  2. swagger.js의 실행 결과로 swagger-output.json 파일이 생성된다. 이 파일은 우리가 생성할 swagger 문서의 구조를 나타낸다.
  3. 생성된 json을 바탕으로 swagger 문서가 생성된다.

 

주의사항.

swagger-output.json은 prestart 스크립트에 의해서 생성되기에 nodemon처럼 저장후 자동으로 서버가 재실행 되는 경우 prestart 스크립트가 실행 안될 수 있다.

즉, 서버를 완전히 종료하고 다시 npm start를 해줘야 prestart가 실행되며 swagger-output.json 파일이 생성(갱신)된다.

 


3. 적용하기

0.  npm install

npm i swagger-ui-express swagger-autogen

 

 

1.  프로젝트 구조

├ src

└─ swagger

      └─ swagger.js

      └─ swagger-output.json

└─ loader

      └─ express.ts

 

swagger.js을 prestart 스크립트로 실행하여 swagger-output.json을 얻어내고,

express.ts에서 swagger-output.json을 참조하여 swagger 문서를 생성하는 함수를 호출한다.

 

 

2.  코드

swagger.js

const swaggerAutogen = require('swagger-autogen')({ openapi: '3.0.0' });

const options = {
  info: {
    title: 'This is my API Document',
    description: '이렇게 스웨거 자동생성이 됩니다.',
  },
  servers: [
    {
      url: 'http://localhost:3000',
    },
  ],
  schemes: ['http'],
  securityDefinitions: {
    bearerAuth: {
      type: 'http',
      scheme: 'bearer',
      in: 'header',
      bearerFormat: 'JWT',
    },
  },
};
const outputFile = './src/swagger/swagger-output.json';
const endpointsFiles = ['./src/loaders/express.ts'];
swaggerAutogen(outputFile, endpointsFiles, options);

securityDefinitions 부분은 JWT를 위한 설정이다.

저 속성을 지정하면 authorize라는 버튼이 생기며 JWT Access Token을 header에 등록할 수 있다.

여러 api들을 요청하고 응답을 확인하기위해 일일이 Access Token을 넣어줄 필요가 없다.

 

보다시피 outputFile의 경로를 지정해 주었고 swagger middleware를 설정할 때 이곳을 참조하면 된다.

endpointsFiles는 router가 위치한 파일이다. 나의 경우 이곳에 최상단 router가 있기에 이곳을 지정했다.

 

├ /api

└─ /user

      └─ /

      └─ /marketing

      └─ /phone

└─ /board

      └─ /

      └─ /admin

└─ /ping

 

이와 같은 router구조를 가진다고 했을 때, /api가 위치한 곳을 가리켜야 swagger가 만들어졌을때 

/api/user/phone

이렇게 전체 경로가 표시된다.

 

 

아무튼 이렇게 생성된 json파일을 가지고 swagger를 생성해준다.

import swaggerFile from '../swagger/swagger-output.json';
import swaggerUi from 'swagger-ui-express';

//Swagger
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerFile, { explorer: true }));

 

 

이제 /api-docs로 접속하면 다음과 같은 화면을 볼 수 있다.

 

 

근데 지금은 모든 api들이 무질서하게 나열돼 있어서 보기 안좋다.

tag를 걸어보자.

tag를 걸면서 request, response 양식도 정해주자.

 

 

다음과 같은 주석을 각 api 함수 내부에 넣어주자. 이게 좀 가독성을 해칠 수 있겠지만 그나마 가장 간단하고 편리한 방법이라고 생각한다...

다음 api는 user의 phone을 수정하는 put api이다.

  /*
   #swagger.tags = ['Users']
   #swagger.summary = '유저 phone 수정'
   #swagger.description = '유저 phone을 업데이트한다.'
   #swagger.security = [{
       "bearerAuth": []
   }]
   #swagger.requestBody = {
        required: true,
        content: {
            "application/json": {
                schema: {
                  "type": "object",
                    "properties": {
                      "phone": {
                        "type": "string",
                        "required": false
                      }
                    }
                },
                example: {
                    "phone": "010-1111-2222",
                },
            },
        }
    }
    #swagger.responses[200] = {
        description: "유저 phone 수정 완료",
        content: {
            "application/json": {
                schema: {
                    "data": {
                        "phone": "010-0000-0000",
                    }
                },
                example: {
                    "data": {
                        "phone": "010-1111-2222",
                    }
                },
            }
        }
    }
*/

 

 

태그를 Users로 지정한 것들은 다음과 같이 이쁘게 그룹화된다.

 

 

또 요청/응답 형식의 예시를 지정해 둘 수 있다.

이렇게 하면 다른 개발자들과 협업하기 편리할 것이다.

나의 경우는 api 함수 내부 가독성을 최대한 살리기위해 200 응답만 예시를 정해두었고 나머지는 생략했다.

 

추가적인 속성들은 위에 첨부한 swagger-autogen 공식 깃허브 문서를 참고하면 쉽게 따라할 수 있을 것이다.

 

 

 

 


4. 마치며

나는 응애개발자라 아직 주석이 가득한 코드가 익숙하지 않다.

자바의 경우 어노테이션으로 깔끔 세련되게 됐던거같은데 노드는 좀 애매한 것 같다..

처음부터 swagger 자동 생성을 찾아볼걸, 괜히 한땀 한땀 주석만 쓰고있느라 시간낭비 한 것 같다. 큿소!

무중단 배포에 대해 알아보자.


1. 무중단 배포란?

말 그대로 애플리케이션의 중단 없이 배포를 하는 것을 말한다. 

애플리케이션은 언제 중단될까?

 

 

v1 서비스가 실행 중일 때 v2 버전을 다운로드 받는다.
v1 서비스를 종료 시키는 시점부터 v2를 시작하기 전까지 애플리케이션은 중단된다.
이렇게 서비스가 중단되는 시간을 다운타임(downtime)이라고 한다.

 

이런 문제를 해결하기위해 무중단 배포라는 개념이 나왔고 크게 3가지 방법이 있다.

 

 

 

 

 


2. 무중단 배포 기법

1. Rolling 배포

롤링 배포는 사용 중인 인스턴스 내에서 새 버전을 점진적으로 교체하는 것으로 무중단 배포의 가장 기본적인 방식이다.

서비스 중인 인스턴스 하나를 로드밸런서에서 라우팅하지 않도록 한 뒤, 새 버전을 적용하여 다시 라우팅하도록 한다.

이를 반복하여 모든 인스턴스에 새 버전의 애플리케이션을 배포한다.

 

위 움짤을 보면

1. 4개의 기존 버전(v0.0.1)이 있다.

2. 그 중에 2개의 라우팅을 끊고 업데이트 버전(v0.0.2)로 업그레이드 한다.

3. 업데이트 버전이 배포가 되면 다시 라우팅을 시작한다.

4. 나머지 2개 인스턴스의 라우팅을 끊고 업데이트 버전을 배포한다.

5. 나머지 2개의 인스턴스의 업데이트가 끝나면 다시 라우팅을 연결한다.

 

장점

  • 인스턴스마다 차례로 배포를 진행하기에 상황에 따라 손쉽게 롤백이 가능하다.
  • 추가적인 인스턴스를 늘리지 않아도 된다.
  • 간편한 관리

단점

  • 새 버전을 배포할때 인스턴스의 수가 감소하기 때문에 사용중인 인스턴스에 트래픽이 몰릴 수 있다. 즉, 서비스 처리 용량을 고려해야 한다. 위 움짤에서 2번과정, 4번과정에서 4개의 인스턴스에서 감당하던 트래픽이 2개의 인스턴스로 몰리는 경우이다.
  • 배포가 진행될때 구버전과 신버전이 공존하기에 호환성 문제가 발생할 수 있다. 위 움짤에서 3번과정에서 업데이트 된 버전2개, 안된버전 2개가 공존하므로 사용자들은 균일한 서비스를 받지 못한다.

 

 

 

 

2. Blue Green 배포

블루는 구버전, 그린은 신버전을 의미한다.

운영중인 구버전과 동일하게 신버전의 인스턴스를 구성한 후 로드밸런서를 통해 모든 트래픽을 한번에 신버전 쪽으로 전환하는 방식이다.

 

장점.

  • 구버전의 인스턴스가 그대로 남아있어서 손쉬운 롤백이 가능하다.
  • 구버전의 환경을 다음 배포에 재사용할 수 있다.
  • 운영환경에 영향을 주지 않고 새 버전 테스트 가능.

 

단점.

  • 시스템 자원이 두배로 필요하다.
  • 새로운 환경에 대한 테스트가 전제되어야 한다.

 

 

 

 

3. 카나리 배포

옛날 광부들이 유독 가스에 민감한 카나리아 새를 이용해 가스 누출 위험을 감지했던 것에서 유래한 것으로 잠재적 문제 상황을 미리 발견하기 위한 방식이다.

신버전을 소수의 유저들에게만 배포를 해보고 문제가 없는것을 확인해가며 점차 많은 유저들에게 배포하는 기법이다.

블루그린 배포와 유사하지만 블루그린처럼 트래픽을 한번에 확 바꾸는 것이 아니라 단계적으로 전환하기에 부정적 영향을 최소화하고 상황에 따라 트래픽 양을 조절하며 롤백할 수 있다.

 

장점.

  • 문제 상황을 빠르게 감지
  • A/B 테스트로 활용 가능

 

단점.

  • 네트워크 트래픽 제어 부담.

 

*A/B 테스트란? 

대조군과 실험군으로 나누어서 특정한 UI나 알고리즘의 효과를 비교하는 방법론

 

 

 

 


3. 정리

1. Rolling 배포

  • 인스턴스를 늘리지 않고,
  • 하나씩 새로운 버전으로 늘리는 기법.
  • 사용중인 인스턴스에 트래픽이 몰리는 문제가 있다.
  • 버전간 호환성 문제가 생기는 순간이 있다.

 

 

 

2. Blue Green 배포

배포 전
배포 후

 

  • 구버전과 같은 환경으로 신버전을 미리 준비.
  • 로드밸런서의 라우팅을 한번에 확! 전환시킴.
  • 구버전의 환경을 재활용하거나 롤백하기 쉬움.
  • 단, 시스템 자원이 두배로 든다.

 

 

 

3. 카나리 배포

  • 소수만 사용하는 환경에 신버전을 배포하고 문제를 관찰한다.
  • 문제가 없으면 신버전으로의 트래픽을 단계적으로 늘린다.
  • 블루그린 배포와 유사. 하지만 블루그린은 한번에 전환, 이건 단계적으로 전환.
  • 문제를 빠르게 발견할 수 있다.

 

 

 

 

 

 

참고:

express 앱을 개발하며 자스/타스에 대해 하나씩 배워가고 있다.

오늘은 기본값 파라미터를 사용해보았다.


1. 내용

파라미터에 기본값 파라미터를 설정하면 값이 없거나 undefined가 전달될 경우 지정한 기본값으로 사용한다.

 

 

예시)

function multiply(a, b = 1) {
  return a * b;
}

console.log(multiply(5, 2));
// expected output: 10

console.log(multiply(5));
// expected output: 5

 

 

같은 개념으로 함수를 파라미터로 사용할 때도 기본값을 지정할 수 있다.

function callSomething(thing = something()) {
  return thing
}

let numberOfTimesCalled = 0
function something(){
  numberOfTimesCalled += 1
  return numberOfTimesCalled
}

callSomething()  // 1
callSomething()  // 2

 

 

앞의 파라미터를 뒷쪽의 파라미터의 기본값으로 사용할 수 있다.

function greet(name, greeting, message = greeting + ' ' + name) {
  return [name, greeting, message]
}

greet('David', 'Hi')                      // ["David", "Hi", "HiDavid"]
greet('David', 'Hi', 'Happy Birthday!')   // ["David", "Hi", "Happy Birthday!"]

 

 

 

 


2. 실제로 사용한 상황

express앱을 개발하며 user의 컨트롤러-서비스 부분을 개발하고 있었다.

서비스에서 sequelize ORM을 사용하고 있기에 쿼리문을 간단하게 생성하여 DB를 조작한다.

 

로직에서 service를 호출하는 컨트롤러에 따라 다르게 조회할 컬럼을 설정해야 했었다.

이때 기본값 파라미터를 설정해주는 것만으로 아주 간단하게 문제를 해결할 수 있었다.

 

export const findOneUserInfo = async (
  uid: string,
  attributes: string[] = ['name', 'uid', 'email', 'phone', 'createdAt'],
): Promise<any> => {
  try {
    const result = await User.findOne({
      attributes: attributes,
      where: {
        uid: uid,
      },
    });
    return result;
  } catch (err) {
    return err;
  }
};

코드를 보면 인자로 uid와 attributes를 받는다. 원래는 attributes가 없었는데 기본값 파라미터로 추가해주었다.

 

findOneUserInfo(uid)처럼 uid만 사용하여 호출했을때는 조회할 컬럼이 기본값 파라미터의 

['name', 'uid', 'email', 'phone', 'createdAt']로 지정이 되어 쿼리가 수행된다.

 

하지만 이 컬럼들 이외로 알고싶다면 

findOneUserInfo(uid, ['address', 'gender']) 이런식으로 인자를 넣어주면 된다.

이러면 기본값 파라미터 값에 ['address', 'gender']가 추가된 것이 아닌 ['address', 'gender']만 조회된다. 주의.

 

 

 

 

참고:

+ Recent posts