개발-사용기/serverless 사용기

2. Typescript & Serverless 사용기 - 프로젝트 구조 생성

mouuaw 2020. 7. 6. 20:34

서버를 구현하면서 적당한 위치에 성격이 맞는 함수들을 묶어놓는 것만으로도 프로젝트를 깔끔하게 유지할 수 있다. 여기에 나오는 프로젝트 구조는 내 맘대로 나눈 것이기 때문에 굳이 따라 할 필요는 없다.

 

다만 tsconfig, webpack 설정을 통해 폴더 접근을 쉽게 해 줄 수 있는데, 이 부분은 좀 쓸만하니 프로젝트에 도입해보는 걸 권장한다.

 

1. 폴더 생성

node_modules
api - 생성
model - 생성
service - 생성
util - 생성
config - 생성
test - 생성
handler.ts
...

6개의 폴더를 추가적으로 생성했는데 어떤 용도로 사용할지 간략하게 정리해보면

 

  • api: api로 등록할 함수들을 작성하는 곳
  • model: model로 사용할 class를 정의해두는 곳. class-validator와 같이 사용하면 꽤 쓸만한 로직을 구현할 수 있다.
  • service: api에 구현할 로직을 한번 더 분리시켜 service에 모아둘 수 있다.
  • util: 다양한 곳에서 사용 가능한 함수를 모아두는 곳.
  • config: 중요한 설정값들(예: DB 접속 정보)을 모아두는 곳. gitignore를 사용해 이 폴더가 다른 곳에 노출되지 않게 한다.
  • test: 테스트 코드를 작성하는 곳. 테스트 코드는 프로젝트의 안전성을 높여준다.

 

파일명은 해당 폴더명을 따라서 다음과 같이 사용하려 한다.

*.api.ts
*.model.ts
*.service.ts
*.test.ts
...

 

2. tsconfig.json 설정하기

tsconfig 설정을 통해 import 경로 사용법을 바꿔보려 한다. 설정이 끝나면 다음과 같이 사용 가능하다.

// 원래는 이렇게 함수에 접근한다.
import { genError, sendError } from '../service/http.service'

// 작업을 마치면 @service 를 통해 접근이 가능하다
import { genError, sendError } from '@service/http.service'

 

tsconfig.json 파일을 다음과 같이 바꿔보자

{
  "compilerOptions": {
    "lib": ["es2017"],
    "removeComments": true,
    "moduleResolution": "node",
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "sourceMap": true,
    "target": "es2017",
    "outDir": "lib",
    
    // 추가된 부분 START
    "paths": {
      "@api/*": [
        "api/*"
      ],
      "@model/*": [
        "model/*"
      ],
      "@service/*": [
        "service/*"
      ],
      "@util/*": [
        "util/*"
      ],
      "@config/*": [
        "config/*"
      ],
      "@test/*": [
        "test/*"
      ]
    }
    //추가된 부분 END
  },
  "include": ["./**/*.ts"],
  "exclude": [
    "node_modules/**/*",
    ".serverless/**/*",
    ".webpack/**/*",
    "_warmup/**/*",
    ".vscode/**/*"
  ]
}

 

 여기까지 설정하면 import는 가능해지지만 실제로 서버를 구동시키면 `Cannot find module` 문구가 나타나며 에러가 나는 걸 알 수 있다. 

 

다음에 나오는 webpack 설정까지 마치면 이제 설정은 마무리가 된다.

 

3. webpack.config.js 설정

webpack.config.js 설정에서 resolve 부분에 alias를 추가하면 이제 우리가 원하는 데로 사용할 수 있다.

module.exports = {
  context: __dirname,
  mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
  entry: slsw.lib.entries,
  devtool: slsw.lib.webpack.isLocal ? 'cheap-module-eval-source-map' : 'source-map',
  resolve: {
    extensions: ['.mjs', '.json', '.ts'],
    symlinks: false,
    cacheWithContext: false,
    
    // 추가된 부분 START
    alias: {
      '@api': path.resolve(__dirname, 'api'),
      '@service': path.resolve(__dirname, 'service'),
      '@model': path.resolve(__dirname, 'model'),
      '@test': path.resolve(__dirname, 'test'),
      '@util': path.resolve(__dirname, 'util'),
      '@config': path.resolve(__dirname, 'config'),
    },
    // 추가된 부분 END
  },
  output: {
    libraryTarget: 'commonjs',
    path: path.join(__dirname, '.webpack'),
    filename: '[name].js',
  },
  target: 'node',
  externals: [nodeExternals()],
  module: {
    rules: [
      // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
      {
        test: /\.(tsx?)$/,
        loader: 'ts-loader',
        exclude: [
          [
            path.resolve(__dirname, 'node_modules'),
            path.resolve(__dirname, '.serverless'),
            path.resolve(__dirname, '.webpack'),
          ],
        ],
        options: {
          transpileOnly: true,
          experimentalWatchApi: true,
        },
      },
    ],
  },
  plugins: [
    // new ForkTsCheckerWebpackPlugin({
    //   eslint: true,
    //   eslintOptions: {
    //     cache: true
    //   }
    // })
  ],
};

 

 상대 경로를 사용하면 파일 이동이나 함수 위치 파악에 시간이 소비되는데, 이렇게 alias 설정을 통해 경로를 고정시켜주면 훨씬 경로를 파악하는데 도움이 되는 걸 경험할 수 있었다. 

 

다음번엔 class-validator를 통한 model생성 및 validation 로직 구현을 정리해보겠다.