Category: HowTo

AWS EC2 에 OpenVPN 서버 구축

리눅스에서 OpenVPN 서버 구축해 보도록 한다. 리눅스 서버는 Ubuntu 24.04 LTS 이다.

아키텍쳐

현재 AWS 아키텍쳐는 다음과 같다.

OpenVPN 를 설치할 서버는 AWS 에 Public Subnet 에 있으며 외부에서 접속을 위해서 EIP(Elastic IP) 를 할당 해줬다. VPC 내에 모든 서버들은 172.31.0.0/16 대역폭에 자동으로 IP를 할당받아 Private IP 를 구성하도록 되어 있다.

이제 사내망 PC 에서 server1 에 Private IP 로 접속을 하기 위한 방법으로 OpenVPN 서버를 설치해보도록 한다.

OpenVPN 작동 방식

OpenVPN 의 작동방법은 NAT 기능을 이용하는 것이다. 먼저, OpenVPN 서버를 설치하고 기동하면 OpenVPN 서버는 서버에 tun0 라고 하는 네트워크 인터페이스 생성한다. 물론 이렇게 생성한 NIC에 네트워크 서브넷을 할당한다. tun0 에 IP를 할당하고 서브넷 라우팅도 함께 구성하게 된다.

이제 OpenVPN 클라이언트를 설치하고 설정파일을 읽어 연결을 하게 되면, OpenVPN 클라이언트는 PC 에 tap0 라고 하는 네트워크 인터페이스를 생성한다. 그리고 이 tap0 에는 OpenVPN 서버로부터 네트워크 관련 정보를 전달 받아서 tun0 와 동일네트워크에 IP, Defaut GW 를 구성하게 된다. 이를 다이어그램으로 표현하면 다음과 같다.

OpenVPN 을 이용하는 이유는 172.31.0.0/16 대역에 있는 서버에 접속하기 위해서다. AWS 아키텍쳐 그림상 server1 에 사내PC가 직접 접속할 방법이 없다. 하지만 OpenVPN 을 이용하면 가능하다. 다음과 같이 접속이 이루어진다.

사내PC(192.168.96.112.66) -> tap(192.168.200.56) —– 인터넷 —–> OpenVPN 서버 tun0(192.168.96.200.1) — NAT –> server1(172.31.0.43)

이러한 흐름을 보면 OpenVPN 서버의 설정에서 OpenVPN 서버와 Client 연결을 위한 네트워크 대역 할당과 server1 의 접속은 OpenVPN 서버를 거쳐서 와야하기 때문에 AWS VPC 네트워크 대역을 알려줘야 하고 OpenVPN 서버는 NAT 서버로 동작해야 한다는(그래서 iptables 을 이용한 MASQUERADE 설정을 한다.) 걸 알게 된다.

OpenVPN 2.6

현 시점에서 OpenVPN 은 2.5 를 기준으로 구분이 된다. 2.6 은 현재 최신 버전으로 사용되고 있는데, 2.5 를 기준으로해서 설정에 많은 변화가 있음으로 만일 처음 구축한다면 2.6으로 하고 호환성을 고려한다면 2.4 를 기준으로 구축하는 걸 권장 한다.

OpenVPN 설치

설치는 Ubuntu 24.04 LTS 에서 기본으로 제공하는 패키지로 설치가 가능하다. 이때 OpenVPN 이 사용할 인증서 생성을 위해서 easy-rsa 명령어도 함께 설치해야 한다.

인증서 생성

OpenVPN 서버는 Client 와 보안통신을 위해서 인증서를 필요로 한다. 인증서를 만드는 것은 그렇게 쉬운 절차가 아니여서 많은 이들이 어려움을 겪는데, easy-rsa 를 이용하면 비교적 쉽게 만들 수 있다.

인증서는 서버 인증서와 클라이언트 인증서가 필요하고 이 둘의 인증서를 만들기 위한 CA 인증서도 있어야 한다.

easy-rsa 명령어만 이용하는게 아니라 디렉토리도 있어야 하는데 /usr/share/easy-rsa/3 에서 작업해야 한다.(중요)

easyrsa 초기화

먼저 easyrsa 를 초기화 해준다. /usr/share/easy-rsa 디렉토리로 이동한다. 여기서 다음과 같이 vars 파일을 만든다.

이 파일은 CSR 내용이기 때문에 상황에 맞게 입력해주면 된다. 그리고 다음과 같이 easy-rsa 를 초기화 한다.

CA 인증서 제작

다음과 같이 root CA 인증서를 제작한다.

root CA 인증서가 생성되었다.

서버 인증서

서버 인증서를 만든다. 서버 인증서를 만들기 위해서는 도메인이 필요하다. 이 도메인은 반드시 있어야 하는건 아니고 서버-클라이언트간 보안을 위한 검증을 위한 것으로 가상으로 입력해줘도 된다.

openvpn.systemv.pe.kr 도메인은 가상으로 그냥 적은 것이다. 굳이 신경쓸 필요는 없다.

클라이언트 인증서 제작

여기서 특징이 있는데, OpenVPN 을 위한 Client 인증서는 사용자마다 제작을 해줘야 한다. 그래서 Client 이름은 ID나 이메일등을 지정해 주면 된다.

Diffie-Hellman key 생성

서버 접속을 위한 Diffie-Hellman key 를 다음과 같이 생성해 준다.

TLS-Auth key 인증 키 생성

다음과 같이 TLS-Auth 키를 생성해 준다.

생성한 키 복사

이렇게 생성한 키들을 OpenVPN 서버에 설정 디렉토리에 복사해 준다. 여기서 주의해야 할것이 배포판마다 설정 디렉토리가 다를 수 있다. Ubuntu24.04 LTS 의 경우에 /etc/openvpn 디렉토리다.

OpenVPN 서버 설정

이제 필요한 파일을 모두 갖췄으니, OpenVPN 설정을 해야 한다. 설정을 어떻게 해야할지 모르기 때문에 OpenVPN 에서 제공하는 Sample 파일을 이용해서 해보자.

이제 server.conf 파일을 다음과 같이 설정해준다.

여기서 AWS VPC CIDR 과 OpenVPN CIDR, DNS 서버 설정등을 주의해서 봐야 한다.

iptables 설정

테스트 하는 서버에는 ufw 명령어가 없다. 만일 ufw 명령어가 있다면 이것을 이용하는 방법으로 해야한다.

iptables 설정을 하는 이유는 MASQUERADE 때문이다. OpenVPN 서버가 NAT로 작동하기 위해서 Iptables 을 이용해 패킷의 흐름을 제어해야 하기 때문이다.

여기서 또 한가지, OpenVPN 서버가 실행중일 때에만 MASQUERADE 설정을 유지하고 실행하지 않을때에는 설정을 꺼야 한다. Iptables 초기화를 하면 설정이 초기화 된다. 이를 위해서 Iptables 설정과 해제 두개의 파일을 작성한다.

먼저 add-bridge.sh 라는 이름으로 쉘 스크립트를 다음과 같이 만든다.

내용이 많지만, 맨 아래 두줄이 핵심이다. tun0 네트워크 장치로부터 192.168.200.0/24 대역으로 들어온 IP에 대해서 enX0 장치를 이용해 172.31.0.0/16 대역으로 패킷을 포워딩 하고 MASQUERADE 설정을 해주고 있다.

OpenVPN 서버가 정지하면 초기화 해주는 remove-bridge.sh 파일을 만들어 준다.

모든 체인을 플러쉬하고 POSTROUTING 룰을 삭제한다.

이렇게 만들어진 파일을 systemd unit 파일인 openvpn@server 에 override 설정으로 추가해준다. override 설정은 파일을 일반 편집으로 해서 추가해주면 자동으로 알아서 추가가 된다.

이렇게 하면 openvpn 시작/중지 때마다 쉘 스크립트가 실행 된다.

시작/중지

이제 시작과 중지를 해보자. 다음과 같이 한다.

OpenVPN 클라이언트 설정

OpenVPN 홈페이지에서 Windows 용 클라이언트를 다운받아 설치한다. 설정 파일은 개인마다 가지고 있어야 해서 전체 시스템에 액세스하는 디렉토리에 넣으면 좋지 않다. 이는 설치가 끝나면 팝업으로 알려주는데, C:\Users\계정ID\OpenVPN\config 이다.

이 디렉토리에 서버에서 작성한 파일을 복사해줘야 한다. 복사해야 할 파일은 다음과 같다.

  • linux.crt (클라이언트 인증서)
  • linux.key (클라이언트 인증서 암호화 키)
  • ca.crt (CA 인증서)
  • ta.key (TLS-Auth Key)

이제 linux.ovpn 파일을 다음과 같이 작성한다.

위와같이 작성하고 OpenVPN Client 에서 설정 파일을 지정하고 연결하기 하면 연결이 된다.

Self Signed 인증서 만들기

이 포스팅은 Self Signed 인증서 만들기 간략한 버전이다. 이론적인 내용은 빼고 OpenSSL 을 이용해서 만드는 방법을 설명한다.

만들기 순서

Self Signed 인증서 만들때에는 순서가 있다.

  1. root CA 를 위한 비밀키 생성(비대칭키)
  2. root 인증서 생성 위한 CSR 생성
  3. root 인증서 생성
  4. Server 인증서를 위한 비밀키 생성(비대칭키)
  5. Server 인증서 생성을 위한 CSR 생성
  6. Server 인증서 생성

대략 위와 같은 순서를 따른다. 비대칭 비밀키를 생성을 위해서 openssl 명령어를 사용한다. 글의 진행을 위해서 a.com 도메인에 대한 인증서를 생성하는 것으로 가정한다.

v3 확장

한가지 알고 가야할 것이 x509 표준에 v3 확장이다. 이 확장은 인증서에 대한 버전을 말하는 것으로 현재 Version 3 에서 확장(Extend) 된 것을 말한다.

v3 확장을 이용하면 SAN(Subject Alternative Name) 을 포함한 인증서를 생성할 수 있다.

이러한 확장을 이용하기 위해서 일종의 명세서 파일을 이용하게 되는데, 인증서 생성에 있어서 이 명세서 파일이 핵심이라고 할 수 있다. CSR 및 인증서 생성에 명세서 파일이 사용된다고 할 수 있다.

root CA 를 위한 비밀키 생성

openssl 를 이용해서 root CA 를 위한 비밀키를 다음과 같이 생성한다.

비밀키가 정상적으로 생성이 된다. 생성된 비밀키에 대한 정보는 다음의 명령어로 확인이 가능하다.

참고로 비밀키를 이용해서 공개키(Public Key) 를 생성할 수 있는데, 다음과 같다.

root 인증서 생성 위한 CSR 생성

CSR 파일을 생성하기 명세서 파일을 이용한다. 명세서 파일은 CSR 만 아니라 인증서 생성에도 사용된다. 명세서 파일을 rootCA.cnf 이름으로 생성한다.

위 명세서는 아무렇게나 만는게 아니며 정해진 규칙이 있다. dn 섹션에는 CSR 작성시 입력할 인증서에 대한 요청자 정보를 입력하는 필드를 지정할 수 있다.

이제 RootCA.cnf 명세서 파일을 이용해서 CSR 파일을 다음과 같이 작성한다.

명세서에 dn 섹션에 내용을 출력하고 있다. CSR 파일이 생성이 되었다.

root 인증서 생성

이제 지금까지 생성된 비밀키와 CSR 파일을 이용해 인증서를 생성한다. 인증서를 생성할때에는 인증서의 사용기간을 정해야 한다.

인증서의 사용기간은 730 일이다. 그리고 -extensions v3_ca 를 인자로 줬는데, 명세서 파일인 RootCA.cnf 파일에 v3_ca 섹션을 사용하라는 뜻이다. v3_ca 섹션에는 인증서에 대한 사용범위, RootCA 임을 증명하는 CA:TRUE 등이 있다.

확인은 다음과 같이 할 수 있다.

Server 인증서를 위한 비밀키 생성(비대칭키)

이제 서버 인증서를 생성해야 한다. 서버 인증서를 위한 비밀키를 다음과 같이 생성한다.

Server 인증서 생성을 위한 CSR 생성

이제 서버 인증서 생성을 위한 명세서 파일을 server.cnf 파일 이름으로 다음과 같이 생성 한다.

RootCA 인증서와 차이점이 보인다. alt_names 섹션이 추가 되었다. 그리고 CA:FALSE 이며 keyUsage 도 다르다. 이 명세서를 이용해서 서버인증서를 위한 CSR 를 생성한다.

예제를 위해서 CSR 입력 내용이 RootCA 와 동일하지만 공인 인증서는 서버CSR 입력 내용이 RootCA 와 같을 수가 없다.

Server 인증서 생성

이제 Server 비밀키와 CSR, RootCA 인증서, RootCA 비밀키를 이용해서 Server 인증서를 생성한다.

생성된 인증서를 확인해 보면 CA:FALSE 로 나타나고, Subject Alternative Name 이 명세서에 내용과 같다.

PKCS12 포맷 인증서 변경

PKCS12 포맷은 Java 시스템에서 사용하는 인증서 포맷이다. 앞서 생성한 인증서는 PEM 포맷 인증서라서 Java 시스템에서 바로 이용할 수 없다. PEM 파일이 있다면 PCKS 포맷 인증서로 변경이 가능하다.

-name 은 Alias Name 을 말한다. 패스워드를 지정하지 않을려면 입력하지 않고 엔터만 치면 된다.

제대로 되었는지 다음과 같이 확인할 수 있다.

SpringBoot 에서는 다음과 같이 application.properties 에 다음과 같이 사용할 수 있다.

React + TypeScript 설치(yarn)

React + TypeScript 설치하는 방법으로 npm 을 이용하는 방법이 있다. 하지만 현 시점, 그러니까 React 버전이 19인 경우 경우에는 npm 명령어 설치할 경우에 오류를 만날 수 있다.

React 설치 오류

보통 React 설치를 위해서 다음과 같이 npx 명령어를 사용한다.

하지만 이렇게 할 경우에 현시점에서 다음과 같이 오류가 발생한다.

이에 대한 해결 방법으로 React 18 로 다운그레이드 하면 된다고 글도 많은데, 프로젝트가 React 18 을 요구하지 않을 경우를 제외하고 다운그레이드는 권장하지 않는다.

오류 원인

React 19 로 넘어오면서 의존성 라이브러리가 교체되었다고 한다. React 18 까지는 web-vitals 를 사용했었지만 19로 넘어오면서 vite 를 사용하도록 되었다고 한다. 변경된 라이브러리로 인해서 React 19 설치 명령어도 변경이 있었다.

Yarn 을 이용한 설치

npm 이 버전이 업데이트 되면서 yarn 의 사용이 많이 준 것도 사실이다. 하지만 현 시점에서 React + TypeScrpt 조합과 web-vitals 를 이용하고자 한다면 yarn 을 이용하는 것이 좋다.

yarn 을 글로벌(Global) 로 설치해 준다.

그리고 yarn 을 이용해서 다음과 같이 명령어를 쳐준다.

yarn 을 이용하면 위와 같이 정상적으로 생성이 가능해진다. package.json 파일을 보면 다음과 같다.

의존성 정보를 보면 React 19 와 web-vitals 를 조합해 설치된 것을 알 수 있다.

VSCode 설정

React + TypeScript 설치가 되었다면 프로그래밍을 해야한다. 대부분 VSCode 를 많이 쓰는데, VSCode 에서 제공하는 확장(Extensions) 를 활용하면 많은 이점이 있다. 이에 대해서 간단하게 알아본다.

ESLint

Create 으로 생성된 React 앱에서는 이미 ESLint 가 포함되어 있다. package.json 파일을 열어보면 eslint 관련 설정이 들어 있는 것을 확인할 수 있다.

이것을 VSCode 에디터 차원에서 지원을 할 수 있는데, ESLint 확장을 설치하면 된다.

이것을 설치하고 난 후에 VSCode 에 세팅(Setting) 에서 관련 내용을 추가할 수 있다.

Settings 에서 ‘eslint: probe’ 로 검색 후에 Workspace 탭을 확인하면 되는데, 여기서 javascript 와 typescript, typescriptreact 등이 있으면 된다.

Code Formatting

코드 포맷팅은 아주 중요하다고 할 수 있다. 여러사람이 협업을 하는 현대의 프로그래밍 작업에서 일관된 코드 포맷팅만큼 중요한 건 없다.

Prettier 는 유명한 코드 포맷터이다. React 앱을 생성하더라도 Prettier 는 설치가 되지 않는다. 다음과 같이 설치해 준다.

하지만 이렇게 하면 에러가 발생한다. npx 를 이용해 React 앱을 생성하려고 했던 거와 동일한 에러가 발생한다. yarn 을 이용해 React 앱을 생성했기 때문에 yarn 을 이용해야 한다.

ESLint 도 코드 포맷관련해 기능을 가지고 있다. Prettier 를 설치하면 이 둘이 충돌이 발생할 수 있어서 이를 피하기 위해서 다음의 플러그인도 함께 설치해 준다.

‘eslint-config-prettier’ 는 ESLint Rule 충돌을 방지해주고 ‘eslint-plugin-prettier’ 는 ESLint 에게 코드 포맷터로 Prettier 를 사용하라고 알려준다.

ESLint 설정에서 Prettier 를 사용하도록 설정을 해준다. package.json 파일에 eslintConfig 세션에서 다음과 같이 prettier 설정을 추가해 준다.

Prettier 설정을 위해서 .prettierrc.json 파일을 다음과 같이 생성한다.

소스코드 작성시에 VSCode 에서 이를 활용할 수 있도록 Prettier 확장을 설치한다.

그리고 Settings -> Workspace 탭에서 Format On Save 를 체크 한다.

또, VSCode 의 Default Formatter 를 Prettier 로 변경해 준다.

마치며

React 19 로 넘어오면서 Vite(비트 라고 발음한다. 영어가 아니라 프랑어라고 한다.) 로 변경되었다. 그래서 npx 를 이용해 React 앱을 생성하면 오류가 발생하는데, yarn 을 통해서 React 19 를 사용하면 기본의 컴포넌트 조합을 그대로 사용할 수가 있다.

또, VSCode 와 통합을 위한 플러그인 설치과 설정등도 간단하게 알아 봤다.

React + Typescript 세팅하기(Webpack)

최근에 React + TypeScript 를 배우고 있는데, 생각만큼 잘 되지 않는 부분도 있고 각각의 의미가 다 있는 만큼 한번 정리할 필요가 있어서 글을 쓴다.

React + TypeScript 를 세팅하는 방법에는 두가지가 있다.

  • Webpack 을 이용하기
  • React App 생성하기

여기서는 Webpack 을 이용해 React + TypeScript 세팅을 해볼 것이다.

Webpack 프로젝트 생성

webpack 은 프로젝트 생성 툴이 아니다. webpack 은 React + TypeScript 와 아무런 상관이 없다. 단지 React + TypeScript 생성을 이용할 수 있는 하나의 방법이다.

webpack 은 CSS, JavaScript 등을 모두 생성해 준다. 특징이 있다면 JavaScript 의 경우에는 하나의 파일로 만들어 준다. 여러가지 파일을 만드는 것이 아니라 오직 한개의 파일로 모두 작성된다.

package.json 파일 생성

nodejs 에 프로젝트 정보파일인 package.json 파일을 생성해 준다. 프로젝트 이름, 의존성, npm 스크립트등을 기재하면 할 수 있지만 일단 최소한의 정보만 입력한다.

nodejs 의 의존성 패키지들은 npm 명령어로 설치하면 자동으로 package.json 파일에 기재된다.

web page 추가

src 폴더를 생성하고 그 안에 index.html 파일을 작성한다.

React 가 실행되면서 속성값인 id=”root” 의 div 에 내용을 추가해 준다.

TypeScript 추가

npm 명령어를 이용해서 TypeScript 를 추가해 준다. 한가지 주의해야 할 것은 결과물은 어짜피 JavaScript 로 나오기 때문에 개발할때만 TypeScript 만 필요하게 된다. 따라서 npm 을 이용해 설치할 때에는 개발목적으로 설치 하는게 좋다. ‘–save-dev’ 옵션을 사용하면 개발목적으로 설치할 수 있다.

위 명령어는 다음과 같이 축약 할 수 있다.

설치를 하고 나면 다음과 같이 package.json 파일에 TypeScript 의존성이 추가 된다.

‘devDependencies’ 는 개발목적의 의존성을 말하며 그 안에 TypeScript 가 있다. 설치된 버전은 최신버전으인데, 설치할때에 버전을 따로 지정해 주지 않으면 최신버전으로 설치가 된다.

이제 TypeScript 를 위한 설정 파일을 root 폴더에 ‘tsconfig.json’ 이름으로 생성해 준다.

몇가지 설정은 다음과 같다.

  • ‘noEmit’ 를 true 로 하게되면 TypeScript 컴파일러가 아무것도 트랜스파일(trnaspilation)을 하지 않는다.
  • ‘allowSyntheticDefaultImports’ 와 ‘esModuleInterop’ 를 true 로 하게 되면 React 를 디폴트 임포트해준다. import React from ‘react’ 를 자동으로 해주는데, false 로 되었을 경우에는 import * as React from ‘react’ 로 임포트 된다.
  • ‘forceConsistentCasingInFileNames’를 true로 설정하면 임포트문에서 참조된 파일 이름의 케이싱이 일관되게 유지되는지 확인할 수 있는 타입 체킹 프로세스가 활성화 된다.

React 추가

이제 React 와 TypeScript 를 설치 한다.

‘react’ 는 react 에 코어 라이브러리다. 모든 React 관련 라이브리가 사용한다. ‘react-dom’ 은 웹 앱(web app) 을 빌드하는데 사용되는 라이브러리다.

React 는 TypeScript 를 포함하지 않는다. 따라서 별도로 설치를 해줘야 하는데, 다음과 같이 해준다.

‘–save-dev’ 옵션을 줘서 개발목적으로만 설치를 한다. 빌드하고 나온 결과물은 Javascript 로 되기 때문에 TypeScript 는 필요가 없기 때문에 개발목적으로만 설치를 하면 된다.

src 폴더에 루트(root) 컴포넌트라고 하는 ‘index.tsx’ 파일을 작성한다.

위 코드는 id 가 ‘root’ 인 DOM 에 React 앱을 주입시켜 준다. 확장자가 js 가 아닌 tsx 로 한 이유는 Babel이 트랜스컴파일을 위해 JSX 를 포함하는 TypeScript 파일을 자동으로 찾도록 하고 TypScript 가 타입 체크 프로세스를 진행할 수 있도록 한다. 확장자가 ts 인 경우에는 TypeScript 코드인데 JSX 를 포함할 수 없다.

‘document.getElementById(‘root’) as HTMLElement’ 에서 ‘as HTMLElement’ 를 type assertion 이라고 하는데, TypeScript 를 이용하기 때문에 어떤 타입인지를 명시를 해줘야 한다. ‘document.getElementById’ 은 ‘as HTMLElement’ 거나 null 타입을 가질 수 있는데, ‘as HTMLElement’ 타입을 가지라고 명시하고 있다.

Babel 추가

Babel 은 React 와 TypeScript 코드를 JavaScript 코드로 트랜스파일(transpile, 혹은 transcompile)을 해준다. 역시 결과물은 JavaScript 만 있으면 되는거라서 개발목적으로만 설치하면 된다.

또, 최신의 JavaScript 기능을 사용하도록 하기 뒤해서 @babel/preset-env 라고 하는 Babel 플러그인을 설치해 준다.

또, React 를 JavaScript 로 변환하기 위해 @babel/preset-react 라고 하는 Babel 플러그인을 설치해 준다.

똑같이 TypeScript 를 JavaScript 로 변환하기 위해 @babel/preset-typescript 라고 하는 Babel 플러그인을 설치해 준다.

마지막으로 JavaScript 에 async 와 await 를 사용할 수 있도록 다음 두개의 플러그인을 설치해 준다.

Babel 관련해 설치는 마무리 되었다. 이제 .babelrc.json 설정 파일을 다음과 같이 작성해 준다.

위 설정은 Babel 에게 설치된 Babel 플러그인들을 사용하도록 하며 설정된 되로 동작하도록 한다.

Webpack 추가

Webpack 은 주로 JavaScript 소스 코드 파일을 함께 묶는 유명한 툴이다. 파일을 스캔하는 것처럼 Babel 과 같은 다른 툴들을 실행시킬 수 있다. 또, 모든 소스 파일을 스캔하고 이것을 JavaScript 파일로 트랜스파일해 준다. Webpack 으로 만들어지는 결과물은 index.html 파일 하나로 작성되며 JavaScript 소스코드로 이 안에 있게된다.

Webpack 설치

Webpack 설치는 다음과 같다.

Webpack 은 ‘webpack’ 패키지 안에 TypeScript 타입을 가지고 있어서 별도로 TypeScript 를 설치하지 않아도 된다.

다음으로 Webpack 개발 서버를 설치한다.

Webpack 개발 서버는 개발하는 동안 사용되는 서버로, 소스코드가 변경되면 자동으로 웹 앱을 업데이트 해준다.

Babel 이 React 와 TypeScript 코드를 JavaScript 로 트랜스파일하도록 Webpack 플러그인을 설치 해줘야 한다. 이 플러그인을 ‘babel-loader’ 라고 한다.

Webpack 은 React 앱을 호스트 하기 위해 index.html 파일을 생성할 수 있는데, 마치 템플릿 처럼 그리고 React 앱들을 index.html 파일로 번들하기 위해서 index.html 파일을 사용할 것이다. 이러한 기능을 위해서 ‘html-webpack-plugin’ 플러그인을 설치해야 한다.

이제 Webpack 과 연관된 라이브러리 설치가 모두 끝났다.

Webpack 설정

Webpack 개발을 위한 설정이 필요하다. 먼저 설정을 TypeScript 형식으로 기술할 수 있도록 ‘ts-node’ 플러그인을 설치한다.

이제 TypeScript 형식으로 Webpack 설정을 할 수 있다. Webpack 개발 설정 파일 이름은 ‘webpack.dev.config.ts’ 로 한다. ts 확정자를 가지고 있기 때문에 TypeScript 를 사용할 수 있으며, 이는 ‘ts-node’ 플러그인 때문에 가능한 것이다.

프로젝트 루트 디렉토리에 ‘webpack.dev.config.ts’ 파일을 다음과 같이 작성한다.

  • ‘import path’ 는 webpack 번들이 어디에 위치하는지 지정할 수 있다.
  • ‘import HtmlWebpackPlugin’ 은 index.html 파일 생성할 수 있도록 해준다.
  • webpack 설정 TypeScript 타입은 ‘webpack’ 과 ‘webpack-dev-server’ 패키지에서 제공된다. intersect type 을 사용해 이를 연결하고 Configuration이라는 타입을 만든다.

‘const config: Configuration’ 에서 Configuration 타입을 사용하는 것을 알 수 있다. 위 설정에 대해서 다음과 같은 의미를 가진다.

  • mode: ‘development’ 는 개발 모드를 지정한다. 이 모드는 React 개발 툴을 포함한다는 뜻이다.
  • output.publicPath 는 앱의 root 패스다.
  • entry 프로젝트에서 React 앱의 엔트리 지점인 index.tsx 가 어디에 있는지를 webpack 에게 알려준다.
  • Webpack 은 configuration 객체를 기본적으로 export 되기를 기대하기 때문에 기본적으로 export default config 를 해줘야 한다.
  • module 속성은 webpack 에게 다른 모듈을 어떻게 처리해야하는지 알려준다. webpack 에게 .js, .ts, tsx 확정자를 가지는 파일에 대해서 babel-loader 를 사용하라고 알려준다.
  • resolve.extensions 속성은 webpack 이 모듈을 해석하는 동안 TypeScript 파일과 JavaScript 파일을 찾을 수 있게 알려준다.
  • HtmlWebpackPlugin 은 HTML 파일을 생성하는데, src/index.html 을 생성하도록 한다.
  • HotModuleReplacementPlugin 는 모듈이 앱을 실행하는 동안에 전체 릴로드 없이 업데이트 하도록 해준다.
  • devtool 속성은 webpack 이 전체 인라인 소스 맵(full inline source map) 을 사용하라고 알려준다. 이는 트랜스파일하기 전에 원본 소스코드를 디버깅할 수 있도록 해준다.
  • devServer 속성은 webpack 개발 서버를 위한 설정이다. 이 설정에 따르면, 웹 서버의 포트는 4000 이며 루트 디렉토리는 dist 폴더다. historyApiFallback 는 deep link 를 위해 필요하며, open: true 는 서버가 시직되면 자동으로 브라우저를 열도록 한다.

이제 webpack 서버를 구동하기 위한 Script 를 package.json 파일에 설정해 준다.

Webpack 웹 서버 실행

이제 모든 준비는 끝이 났다. 다음과 같이 웹 서버를 실행 시켜 준다.

정상적으로 실행이 되었다면 자동으로 브라우저가 실행되면서 화면이 출력된다.

React + TypeScript 설치 방법중에 한가지로 Webpack 을 이용하는 방법에 대해서 알아봤다.

Harbor 업데이트 하기

현재 개인적으로 Harbor 를 사용하고 있다. 별로 중요하지 않은 프로젝트여서 크게 신경쓰지 않는 저장소인데, 그래도 이래저래 테스트를 하는데 유용하다. Harbor 를 사용하다가 새로운 버전이 출시되어서 어떻게 업데이트를 했는지를 기록으로 남겨둔다.

Harbor Stop

Harbor 를 정지 시켜야 한다. Harbor 는 Docker-compose 기반으로 작동됨으로 docker-compose 명령어를 이용해서 다음과 같이 정지를 시켜줘야 한다.

Backup

혹시 잘못됐을때를 대비해 Backup 을 해준다. 디렉토리를 그냥 복사해주는 것으로 하면 된다.

새로운 버전 다운로드

Docker 명령어를 이용해서 새로운 버전의 harbor 이미지를 다운로드 한다.

데이터베이스 Migration 하기

기존의 데이터베이스를 새로운 버전으로 Migration 을 해야 한다.

데이터베이스 마이그레이션할 내용이 없으면 그냥 끝나게 된다.

새로운 버전 다운로드 후 설치

새로운 버전의 Harbor 을 다운로드 하고 난후, install.sh, common.sh, prepare 파일을 기존 디렉토리에 복사해 준다.

설치

이제 새로운 버전을 설치를 한다.

크게 어려운건 없다. 순서대로 했는데 중간에 문제가 되거나 하지 않았다. 업데이트를 한 후에 저장소를 사용하고 있는데, 기존 자료가 이상해지거나 되는 기능이 되지 않는 문제는 없었다.

유용한 Git 명령어

유용한 Git 명령어 정리.

Git 설정

대부분 Git 설정은 명령어로 한다.

하지만 Git bash 를 이용할 경우에 설정 파일을 직접 편집하도록 할 수도 있다.

브랜치 리스트

로컬 브랜치 삭제

원격 브랜치 삭제

원격 브랜치 삭제는 branch 명령어를 사용하지 않는다.

로컬에 원격 브랜치 목록 업데이트

원격 브랜치 목록 보기를 했을때에 실제 원격 브랜치와 차이가 있을 수 있다. 이것때문에 헷깔릴 수 있는데, 원격 브랜치 목록을 로컬에 반영하기 위해서는 다음과 같이 해준다.

prune 을 자동으로 되도록 설정할 수 있다.

git 설정 파일에서는 다음과 같이 설정할 수 있다.

node_exporter 설치하기

node_exporter 는 OS 에 대한 각종 지표를 수집해주는 exporter 다. Prometheus 가 읽어 갈수 있도록 작은 웹서버로 작동된다.

설치야 바이너리로 배포를 하기 때문에 아키텍쳐에 맞게 다운받아서 설치를 하면 된다. 압축 풀고 시작하면 그만일 정도로 아주 간단하다. Prometheus 에 exporter들은 대부분 간단하다. 복잡하게 설치하지는 않는다.

그런데… 고려해야하는 부분이 존재한다. 일단, 설치부터…

Download & Install

다운로드는 Github 저장소에 받으면 된다.

node_exporter 디렉토리가 보이고 그 안에 node_exporter 바이너파일이 있다. 이것을 적당한 곳으로 이동시키놓으면 끝난다.

어떻게 시작/중지 할건지…

여기서 이제 고민을 해야한다. 많은 사람들은 이것을 쉘 스크립트와 nohup 을 사용하는 사람들이 있다. 어느 시대에 살고 있는 사람들인지 의심이 될 정도인데, 이제는 init script 도 다 없어질 만큼 대부분의 배포판들이 systemd 로 다 전환이 완료된 상태다.

그러면 당연히 systemd 로 하면 되지! 하지만 여기서 문제가 된다.

현재 systemd 는 지속적으로 지금도 버전업이 되고 있다. 그러다보니 특정 버전을 기준으로 특정 기능이 지원이되고 안되고가 갈리게 된다.

systemd 버전 240 …. (대체 어떻게 버전 관리를.. 어떻게 기능을 많이 집어넣었으면 버전이 240 이여.. -_-;; ) 왠만하면 systemd 버전 240 이상을 사용할 것을 권한다. 그런데, 이게 말처럼,, 240버전을 써라~~ 한다고 되는게 아니다.

systemd 는 리눅스 시스템의 뼈대라고 보면된다. 핵심중에 핵심! 그러다보니 systemd 는 배포판과 함께 제공되고 묶여 있다. 240버전을 쓰고 싶다면 240버전을 가진 배포판을 써야 한다는 뜻이 된다.

  • Ubuntu 22.04: 249.11-0ubuntu3.12
  • RHEL 8.10: 239-82.el8_10.1

이런 저런 사유로 배포판을 선택하고 거기다 버전을 선택하게 된다. 내가 하고 있는 프로젝트에서는 CentOS, RHEL 7.9 가 대부분이고 RHEL 8 은 최신형으로 취급하는데.. systemd 만 놓고 보면 RHEL 8 도 그다지 마음에 들지 않는 부분이다.

개인적으로 RHEL 8 도 이제는 끝물이다. RHEL 9 의 버전이 이제는 벌써 9.3을 벗어나고 있기 때문에 이제는 RHEL 9 로가야 한다.

아무튼, 말이 길었는데, systemd 유닛으로 만들어 보자..

일단, node_exporter 에는 많은 옵션들이 있다. Prometheus exporter 들이 많은 옵션들을 제공한다. 이러한 옵션들은 별도의 파일로 작성하고 쉘 변수로 만들어 두고 systemd 유닛에서 읽어들이도록 하면 된다.

/etc/sysconfig/node_exporter 파일에 다음과 같이 내용을 적어준다.

/etc/sysconfig/node_exporter 는 RedHat 기반에 적합하다. Ubuntu 면 /etc/sysconfig 디렉토리가 없기 때문에 Ubuntu 레이아웃에 맞는 곳에 넣으면 된다.

systemd 유닛 파일은 별거 없다.

딱 보면 별거 없다….. 하지만,,, 240 이하에서는 출력되는 로그들… 일명 Standard Out 들을 어떻게 처리할까? 그냥 이대로 둬도 되나? 결론은 되긴 한다. 이렇게 그대로 두면 node_exporter 가 뭔가를 출력하면 stdout, stderr 로 내보낸다. 그러면 systemd 는 이것을 /var/log/syslog 파일에 기록하게 된다.

하지만, systemd 로 변경되면서 나온 journal 에 기록을 하고 싶을지도 모른다. 이왕이면 그렇게하는게 좋기도 하다. 그래서 다음과 같이 [Service] 세션에 StandardOutput 옵션을 준다.

콘솔에도 출력을…. 뭐.. 이건 옵션이다. 여기서 systemd 버전에 따라서 파일에 redirect 가 가능하기도 하고 불가능하기도 하다. StandardOutput 에 옵션이 inherit, null, tty, journal, kmsg, journal+console, kmsg+console, file:path, append:path, truncate:path 이렇게 되어 있다. 이게 다 가능한게 아니다. 버전에 따라서 가능하기도 하고 불가능하기도 하다.

Systemd 버전

새로운 배포판에 따라서 systemd 의 버전이 달라진다. 문제는 대부분 systemd 버전이 특정 시점까지만 업데이트가 된다. 시스템에 뼈대이다 보니 확 갈아 엎을 수 없는 것이여서 그럴거다.

이러다보니 특정 기능을 탐이 나는 때가 있는데, OS 를 다 갈아 엎어야하는 고충이 있다. 그래서 이왕이면 새로운 시스템을 구축할때에는 왠만하면 최신판을 쓰는게 좋다. Ubuntu 라면 24.04, RedHat 이면 9.0 을 사용하길 권한다.