
설계란?
- 프로그래밍에서의 코드 설계는 코드와 파일, 그리고 폴더 구조를 설계하는 것을 말합 니다.
만약 적절한 설계를 하지 않고 코드를 작성하게 될 경우 여러가지 문제가 발생할 수 있습니다.
코드가 몇백 줄(혹은 몇천 줄)이 되기 때문에, 수정할 코드가 있을 때 해당 부분을 찾기 힘듭니다. 따라서 유지보수가 어려워집니다.
코드와 코드 사이 (함수와 함수 사이 등) 어떤 관계가 있는지 한 눈에 알아보기 힘듭니다.
각 코드(함수, 객체 등)의 역할과 기능이 명확하게 구분되어 있지 않으므로, 기능별로 테스트(유닛 테스트)를 진행하는 것이 어려워집니다.
하나의 파일을 동시에 여러 사람이 수정하는 것은 힘들기 때문에, 분업이 어려워 집니다.
새로운 기능을 추가하고자 할 때, 기존에 존재하는 코드의 어느 부분을 수정하고 혹은 새로 작성해야 하는지 알아내기 힘듭니다. 즉, 확장성이 부족한 코드가 됩니다.
위 문제들을 해결하기 위해 다양한 코드 구조 설계가 만들어졌는데, 저는 그 중 3계층 구조 설계를 사용하기로 했습니다!
3계층 구조 설계란 3개 구조로 나누어 설계하는 것을 말합니다.

Control layer (컨트롤러): 사용자의 요청(request)을 분석한 후, 알맞은 서비스로 해당 요청을 전달해 준 다음, 서비스의 결과를 다시 응답(response)하는 층입니다.
즉, 라우팅(Routing)이 이루어지는 층입니다.
express의 경우,req.params(), req.body(), res.status(), res.send()와 같은 코드가 작성됩니다.
Service layer(서비스): 컨트롤러로부터 전달된 요청에 로직을 적용하는 층입니다.
이 때 로직이라는 것은, 예를 들어 로그인 서비스의 경우 다음과 같습니다.
아래와 같은 로직을 if-else 등의 코드로 구현할 수 있습니다.
Model layer(데이터): 서비스 층에서 데이터베이스 접근이 필요한 경우가 있는데, 이 때 데이터 관련 코드가 작성되는 층입니다. Mongoose의 경우 Model.find({})와 같은 코드가 작성됩니다.
로그인 서비스를 예시로 보면, 요청으로 온 ID가 데이터베이스에 존재하는지, 만약 존재한다면 그 때 데이터베이스 상에 저장된 비밀번호는 요청으로 온 (사용자가 입력한) 비밀번호와 일치하는지 확인해야 합니다.
이 때 서비스 층은 데이터 층에 데이터베이스 확인 요청을 하게 되는 것입니다.
위와 같이 코드를 역할별로 분리하여 각 층에 구현하는 경우, 다음과 같은 장점이 있습니다.
각 역할별로 개발 업무를 분담할 수 있으므로 분업이 용이해집니다.
역할 별로가 아닌 MVP 별로 개발 업무를 분담하는 경우에도, 각 MVP가 어떤 흐름으로 구현되는지 (Controller에서 사용자로부터 요청을 받고, 요청에 대해 Service 층에서 로직을 구현하고, 필요 시 데이터베이스에 접근한 후, 결과를 Controller가 응답합니다) 코드 구조를 보고 이해하기 쉬워집니다.
라우팅 관련 코드는 Controller 폴더에서, 서비스 로직 관련 코드는 Service 폴더에서, 데이터 관련 코드는 Model 폴더에서 구분하여 확인할 수 있으므로, 필요한 코드를 빠르게 찾을 수 있으며, 따라서 유지보수가 용이해집니다.
웹 서비스의 구현 방식을 변경하고자 할 때, 예를 들어 데이터베이스를 Mongodb에서 Mysql로 변경하고자 할 때, 각 폴더는 역할이 분리되어 있으므로, Model 폴더 코드만 변경하면 되고 나머지 Controller, Service 폴더는 변경하지 않아도 됩니다. 유지보수가 용이해집니다.
코드가 기능별로 구분되어 있으므로, 기능별로 테스트(유닛 테스트)를 진행하기 용이해집니다.
그래서 이런 3계층 구조 설계를 사용하기 위해 현재 폴더구조를 아래와 같이 설정했습니다.
└───src
├───db # Model layer
│ ├───models
│ └───schemas
├───middlewares
├───routers # Control layer
└───services # Service layer
처음 설계할 때 User DB에 배열로 수상 내역을 넣는 식으로 진행 했으나 Award 부분 관리를 하는데 User DB 부분을 같이 봐야하는 점이 불편해서 User Model 과 Award Model을 따로 나눈 후 Award 모델에서 User모델을 참조하게 하여 진행하였습니다.
import mongoose from "mongoose"; const Schema = mongoose.Schema; const model = mongoose.model; const AwardSchema = new Schema({ id: { type: String, unique: true, required: true, }, title: { type: String, required: true, }, description: String, author: { type: Schema.Types.ObjectId, ref: "User", required: true, }, }); const AwardModel = model("Award", AwardSchema); export { AwardSchema, AwardModel };
그리고 담당한 다른 파트인 Education [학력 부분 API ] 의 모델도 Award Model 처럼 설정해주었습니다.
Gitlab 이슈를 생성 하게 되면 이슈 번호가 지정되는데, Commit 을 할 때 특정 명령어와 이 이슈번호를 사용하면 Commit을 할 때 이슈를 자동으로 닫게 할 수 있습니다.
만약 이슈 번호가 #79번 일때, "close #79" 또는 "fix #79" 처럼 입력하게 된다면 commit 을 하면 이슈도 자동으로 닫히게 됩니다.
만약 다른 방식으로 닫게 하고 싶거나 추가하고 싶다면 config/gitlab.yml 의 issue_closing_pattern 에 지정하면 됩니다.
DB 설계를 그냥 머리에서 떠오르는 대로 시작했더니 결국 DB를 다시 뜯어고치는 일이 생겼다.
처음부터 DB 설계를 짜고, 진행할 일 들을 미리 구상했으면 뜯어고치는 일을 없애거나 최소화 할 수 있을 것 같아 다음번엔 꼭 진행할 예정에 맞춰 설계를 한 후, 스키마를 작성해야겠다.
그리고 이슈를 작성하고, commit 하고, merge 한 후, 이슈를 직접 닫아 줬었는데 GitLab에서 작성한 이슈를 commit 으로 자동으로 닫게 할 수 있다는걸 알고 역시 아는 만큼 편하고 효율성 높게 작업할 수 있다는 걸 다시 한번 느껴 내가 사용하는 툴 들의 기능을 한번 더 공부해봐야겠다.
처음 3계층 구조 설계로 폴더를 나눈 후 코드를 작성하는데 여기서 작성하고 저기서 작성하고 처음에는 헷갈리고 힘들었지만, 적응하고 나니 내가 원하는 부분을 편집할 때 딱 그 부분만 가서 수정하면 되는 것을 몸으로 느끼자 평소에 한 곳에 짜는 것 보다 훨씬 편하게 느껴졌다.
앞으로 다른 프로젝트를 진행할 때는 다른 구조도 사용해봐야겠다.