개발-사용기/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 로직 구현을 정리해보겠다.