Dwitter – 서버 코드에서 중요한 두가지 (5)

보안성

현재의 코드에서 확인해보면, ‘jwtSecretKey’ 등 중요한 키값들이 코드내에 그대로 사용되고 있는 것을 볼 수 있다. 이것은 보안성에 문제가 된다. 보통 코드들을 코드 관리 툴(git 등)에 올리는데, 이 과정에서 이 키값들이 노출된다면 해커가 언제든지 jwt 의 payload를 해석하여 악용할 수 있다.

설정성

또한, 포트 번호나 jwt 토큰의 만료시간과 같은 설정값들이 코드내에 그대로 삽입되어 있다. 이렇게 사용할 경우 처음 배포하고 나서 여러가지 설정을 변경하고 재 배포할때, 코드내에서 개발자가 직접 변경하고 컴파일하고, 배포하는 등의 과정이 필요하기 때문에 비효율적이다. 특히 모바일 어플리케이션의 경우, 앱스토어나 구글 플레이스토어에 업데이트하는 시간이 아주 길다.

그렇기 때문에, 어플리케이션을 만들때는 코드를 바꾸지 않고도 설정값들을 쉽게 바꿀 수 있도록 만드는 것이 아주 중요하다!


환경변수 등록

export 

서버에서 필요한 모든 설정값은 서버의 환경변수 형태로 등록해두는 것이 좋다. 이렇게 처리하면, 서버가 동작하면서 코드내에 입력된 고정된 값을 이용하는게 아니라 환경변수에 있는 설정된 값을 읽어온다. (git 등의 소스 버전 관리 툴을 이용하더라도 중요한 정보가 밖으로 유출되지 않는다)

환경변수 형태로 등록했을때의 문제점 ❓

터미널을 새로 시작했을때, 더이상 환경변수에 등록된 값이 남아있지 않다. export 로 process.env에 등록시, 현재 동작하고 있는 터미널 세션에 한해서만 환경변수에 추가된다. 그렇다면 서버를 시작할때마다, 일일이 환경변수를 export를 이용하여 환경변수를 등록해줘야할까 ? 또한 운영체제마다 환경변수를 등록하는 방법이 달라질 수 있는데 이것은 어떻게 처리할까 ?

환경변수를 편하게 등록하는 방법✅

dotenv 미들웨어를 사용한다.

npm i dotenv

.env 파일에 key, value로 정의해두면 프로그램이 동작하면서 이 값들을 환경변수에 적용해준다. 최상위 경로에 .env 파일을 생성후, .gitignore 파일에서 해당 파일이 업로드 되지 않도록 설정하면 된다.

JWT_SECRET={secret key}
JWT_EXPIRES_SEC={expires seconds}
BCRYPT_SALT_ROUNDS=12
import dotenv from "dotenv";
dotenv.config(); // 최상위 경로에 있는 .env 파일을 읽어와서 환경변수로 등록해줌

조금 더 효율적으로 사용하는법

각각의 코드에서 process.env.{} 으로 호출하여 사용해도 되지만, 이 변수들은 서버가 시작될때 할당되는 변수들이기때문에 안에 어떤 변수가 들어있는지 코딩하는 시점에는 알수가 없다. 오타가 날 확률이 높고, 정말 중요한 키인데 정의가 되었는지 안되었는지 확인할 방법이 없다.

import dotenv from "dotenv";
dotenv.config();

export const config = {
  jwt: {
    secretKey: process.env.JWT_SECRET,
    expiresInSec: process.env.JWT_EXPIRES_SEC,
  },
  bcrypt: {
    saltRounds: process.env.BCRYPT_SALT_ROUNDS,
  },
};

위와 같이, 필요한 구간에서 config 파일을 호출하여, 등록된 환경변수를 사용하는 것이 좋다. (자동 완성 > 오타가 날 위험이 없음)


개발하는 단계에서 환경변수 적용 여부 확인하는 로직 개선

import dotenv from "dotenv";
dotenv.config();

function required(key, defaultValue = undefined) {
  const value = process.env[key] || defaultValue; // .env에 등록되지 않았으면 defaultValue로 설정
  if (value == null) {
    throw new Error(`Key ${key} is undefined`);
  }
}

export const config = {
  jwt: {
    secretKey: required("JWT_SECRET"),
    expiresInSec: required("JWT_EXPIRES_SEC", 86400), // default value를 넘긴다.
  },
  bcrypt: {
    saltRounds: required("BCRYPT_SALT_ROUNDS", 12),
  },
};

required 함수를 호출하여, 설정되어있지 않아도 기본값으로 설정되도록 , 또한 기본값으로 설정되지도 않았으면 에러를 리턴하도록 코드를 개선한다.