본문 바로가기
백엔드 개발

#044. 리팩터링: 객체 지향 프로그래밍의 캡슐화 적용하기 💊

by iamjoy 2023. 7. 2.

개념

운영을 하면서 정책들과 구현방법은 복잡해진다. 객체 지향 프로그래밍의 네 가지 특징 중 캡슐화를 고려하며 구현하면 복잡성을 낮출 수 있다. 최근에 이미지 url를 포함해 조회하는 작업을 하면서 캡슐화를 다시 한 번 생각해보는 계기가 되어 기록해두려고 한다.

적용

이미지 파일은 대부분의 이미지는 글의 이미지, 유저의 썸네일 이미지 처럼 연관관계를 가진다. 따라서 연관관계를 가지는 대상을 조회할 때 이미지를 함께 불러오게 된다. 그리고 Response 에는 File을 규칙에 따라 url로 만들어 string 형태로 전달한다. 이 때 어디에서 File 클래스를 url로 변환하면 될까?
예를 들면 아래와 같이 student 테이블에 매핑된 Student 객체가 있고, thumbnail 이라는 필드에 이미지 (File 엔티티)와 연관 관계를 맺었다고 가정한다.

@Entity({ tableName: 'student' })
export class Student {
	...
    
	@ManyToOne(File)
  	thumbnail?: IdentifiedReference<File>;
}

이제 이미지를 Response 까지 전달 할 때 두 가지 방법이 있다.
(1) student.thumbnail(또는 getter)로 정보를 꺼내어 File로 쭉 들고다니다가 response body에서 url 규칙에 맞게 변환해준다.
(2) student 객체에서 thumbnail File을 url 규칙에 맞게 변형해 string 형태의 url 로 전달하고, 이를 response body까지 쭉 사용한다.

이미지 File을 url로 전환하는 로직은 클라이언트에게 데이터를 전달하는 response 가 알아야하는 로직이 아니라고 생각했다. 이미지가 어떤 방식으로 url로 변환될 것인가는 1. 그 로직이 각 도메인 (예를 들면 student, feed, course 등)마다 다르다면 각 도메인들이 2. 서버 전체에서 공통적으로 사용하고 있다면 File 객체가 알고 있어야하는 내용이다. 이것이 어떻게(how) 변환되는 지는 객체 내부에 은닉되어야 한다. 객체 바깥에서는 외부로 노출된 매서드(what)를 가져다 쓸 뿐, 그 안의 구현에 대해서는 알 필요가 없다.

https://radait.tistory.com/5

그런데, 나는 이 작업을 하면서 "File 클래스로 전달하는 것이 좋지 않은 것 같다"라는 리뷰에 -> "이 File 객체가 어디서 어떻게 바뀌는 지 외워둬야겠다"라는 최악의 방향🔥으로 생각했었다. 개발자가 외울 내용이 많아진다는 것은 레거시가 늘어났다는 것을 의미한다. 이후에 해당 PR을 다시 보면서 이 작업은 내가 더 잘 외울 수 있도록 노력하는 것이 아니라 객체 지향적으로 해결해야하는 것이구나 깨달았다. File을url string으로 바꾸는 구현 책임이 있는 객체를 student로 하고 그 객체에서 어떻게(How)를 처리하고 무엇(What)에 관한 작업인지를 매서드 명에 담아 외부로 노출하도록 수정했다.

@Entity({ tableName: 'student' })
export class Student {
	...
    
  @ManyToOne(File)
  image?: File;
    
    
  getImageUrl(): string { // <--- 외부에서는 File이 아니라 이미 만들어진 url string을 사용하도록 한다
    return this.getThumbnail()?.fullUrl(); // <--- 어떻게 url을 바꾸는 지는 Student 내부의 매서드가 알아서 한다.
  }
}