Krong Dev.
아카이브 목록

Supabase 환경에서는 왜 DTO를 따로 정의하지 않을까?


기존 흔히 익숙한 전통적인 백엔드 환경(NestJS, Spring 등)에서는 DTO가 필수입니다.

여기서 DTO는 Data Transfer Object로 계층 간에 데이터를 전달하기 위해 사용하는 객체(Object)를 말합니다.

왜 DTO가 필수일까요?

앞서 말씀드린 ‘전통적인’ 백엔드 환경에서는 데이터베이스의 테이블과 매핑되는 Entity(엔티티) 클래스가 존재합니다.

Entity 클래스 내부에는 @Column, @ForeignKey, @Index, 등 같은 DB 구동을 위한 복잡한 코드와 설정들이 들어가 있기 때문에 이걸 클라이언트에 그대로 내보내면 문제가 발생합니다.

영속성 관심사 분리

  1. 비밀번호, 주민번호같은 민감한 필드가 그대로 api 응답으로 노출될 위험이 있다.
  2. database 테이블 구조를 바꾸는 순간, 프론트엔드가 받는 api 스펙까지 강제로 깨져버린다.

필요한 데이터가 다르다

  1. 사용자 엔티티는 필드가 20개인데, 특정 화면에서 막상 필요한 데이터는 2개뿐이다.

따라서 DTO를 만들어서 필요한 데이터만 골라서(TypeScript의 ‘Pick’/‘Omit’) 내보내는 과정을 거칩니다.

하지만, Supabase에서는 얘기가 다릅니다.

현재 진행중인 “MENHERING” 프로젝트는 Supabase를 쓰고 있고, 이 Supabase는 ‘전통적인’ 백엔드 서버와 데이터 흐름이 완전히 다릅니다.

간단하게 소개해드리면,

  1. Entity 클래스 자체가 없다.

앞서 말씀드린 전통적인 백엔드와 다르게, Supabase에서는 직접 class UserEntity { ... }와 같은 복잡한 백엔드 코드를 짜지 않습니다. 대신 테이블 모양 그대로 생긴 단순한 JSON 객체 row가 결과로 나옵니다.

  1. Supabase가 자체적으로 필드를 필터링해준다.

앞서 말씀드린 두 번째 이유가 됐던 화면마다 다른 필드가 필요할 때, Supabase는 굳이 백엔드 코드 단에서 Pick/Omit 같은 타입 변환 도구를 쓰지 않아도 됩니다. 이러면 프론트엔드나 중간 레이어에서 타입을 쪼개고(types/ or schemas/) 결합하는 복잡한 설계와 이점이 대폭 줄어듭니다.

“그러면 무조건 Supabase를 써야겠네!”

그건 아닙니다. 결국 “Silver Bullet“은 없는 법이죠.

분명 Supabase는 위와같은 귀찮은 작업을 없애주고, 프론트엔드 개발자에게 엄청난 생산성을 준 만큼, DB 구조가 비즈니스 로직과 매우 밀접하게 맞닿아있어, 서비스 규모가 커지고, 비즈니스 규칙이 많아질수록 유지보수에 엄청난 어려움을 겪을 수 있습니다.

따라서 프로젝트의 규모와 도메인 복잡도를 고려해 적합한 아키텍처 선택을 해야 하는 것이 앞으로도 가장 중요한 문제이지 않을까 생각합니다.

  • #배움일기
  • #Supabase