2024-03-28 18:59:13

클린 코드란?

클린 코드는 간결하고 이해하기 쉽고 수정이 용이한 코드를 말합니다.

가독성을 높이고 그에 따라 유지보수도 쉽고 또 에러가 발생할 가능성이 적기 때문에 협업에 있어서 매우 중요하다고 할 수 있습니다.

코드를 작성할 때 항상 고민하지만 막상 다른 사람에게 코드를 보여주거나 설명을 하기 위해 코드를 다시 보면 다른 사람이 보고 이해할 수 있을까 라는 생각이 들 때가 많아 클린 코드에 대해 정리해 보겠습니다.

 

클린 코드의 주요 원칙

가독성 : 다른 개발자가 코드를 읽고 이해하기 쉬워야 합니다.

간결성 : 코드는 가능한 짧으면서 하나의 기능만 수행해야 합니다.

재사용성 : 코드는 재사용 가능하도록 일반적인 형태로 작성되어야 합니다.

테스트 용이 : 코드를 테스트하기 쉬워야 합니다.

유지보수성 : 코드는 변경하기 쉬워야 하며 결합도를 낮춰야 합니다.

 

가독성

함수 이름과 변수의 명이 명확하지 않아 어떤 기능을 수행하는지 알 수 없기 때문에 명확히 해야 합니다.

// 배드 코드
async a(id: number): Promise<User> {
  return await this.b.findOne(id);
}

// 클린 코드
async findUserById(userId: number): Promise<User> {
  return await this.userRepository.findOne(userId);
}

 

간결성

가급적 하나의 함수는 하나의 작업을 수행해야 합니다.

// 배드 코드
// 얼핏 보면 하나의 기능을 수행한다고 보여지지만 2개의 기능을 수행
async updateUser(id: number, userData: UpdateUserDto) {
  const user = await this.userRepository.findOne(id);
  user.name = userData.name;
  user.email = userData.email;
  await this.userRepository.save(user);
}

// 클린 코드
async updateUser(id: number, userData: UpdateUserDto) {
  const user = await this.findUserById(id);
  this.updateValues(user, userData);
  await this.userRepository.save(user);
}

// 함수를 더 작은 단위로 분리하여 한 가지 작업만 수행하도록 수정
updateValues(user: User, userData: UpdateUserDto) {
  user.name = userData.name;
  user.email = userData.email;
}

 

재사용성

특정 상황에서만 사용될 수 있는 함수는 재사용성이 떨어지기 때문에 지향해야 합니다.

// 배드 코드
// isUsed가 true인 상황에서만 동작
getActiveUsers() {
  return this.userRepository.find({ where: { isUsed: true } });
}

// 클린 코드
getUsersByStatus(isUsed: boolean) {
  return this.userRepository.find({ where: { isUsed } });
}

 

테스트 용이

외부 서비스에 강한 의존성을 가지고 있으면 테스트하기 어렵습니다.

// 배드 코드
async sendEmail(user: User, message: string) {
  const emailService = new EmailService(); // 직접적으로 객체를 생성하면 강한 의존성을 띔
  await emailService.send(user.email, message);
}

// 클린 코드
// 의존성 주입을 통해 외부 서비스를 가져와서 사용
constructor(private emailService: EmailService) {}

async sendEmail(user: User, message: string) {
  await this.emailService.send(user.email, message);
}

 

유지보수성

높은 결합도를 가지게 되면 코드 수정 시 다른 부분에도 영향을 미칠 수 있기 때문에 책임을 분리하는 것이 좋습니다.

// 배드 코드
// 유저를 삭제하기 전 해당 유저가 작성한 게시글을 모두 삭제
class UserService {
  async deleteUser(id: number) {
    const posts = await this.postRepository.find({ userId: id });
    await this.postRepository.remove(posts);
    await this.userRepository.delete(id);
  }
}

// 클린 코드
// 게시글을 삭제하는 기능과 유저를 삭제하는 기능을 분리하여 유지보수성 향상
// 각각 해당하는 서비스에서 동작
class PostService {
  async deletePostsByUser(userId: number) {
    const posts = await this.postRepository.find({ userId });
    await this.postRepository.remove(posts);
  }
}

class UserService {
  constructor(private postService: PostService) {}

  async deleteUser(id: number) {
    await this.postService.deletePostsByUser(id);
    await this.userRepository.delete(id);
  }
}

 

NestJs에서 클린 코드 작성 방법

모듈화와 컴포넌트 기반 설계 : 기능별로 모듈을 분리하여 각각의 모듈이 하나의 책임을 갖도록 설계하는 것이 바람직합니다.

서비스와 컨트롤러의 분리 : 비즈니스 로직은 Service, Controller에서는 요청을 받아 Service에 전달하는 구조로 용도를 명확히 해야 합니다.

의존성 주입 사용 : 결합도를 낮추고 테스트에 용이한 코드를 작성해야 합니다.

환경 설정 관리 : 환경 설정 파일을 환경 별로 분리하여 유연성 있게 관리해야 합니다.

코드 일관성 : Prettier나 ESLint 같은 도구를 사용하여 코드 스타일을 일관되게 유지해야 합니다.  

 

 

728x90

'Nestjs' 카테고리의 다른 글

[NestJs] config 사용하기  (0) 2024.03.30
[NestJs] 로그인 기능 구현  (0) 2024.03.23
[NestJs] Repository 패턴 사용해보기  (0) 2024.03.20
[NestJs] TypeORM 연결하기  (3) 2024.03.18
[NestJs] Middleware 알아보기  (1) 2024.03.15