다른 프로젝트 트랙을 하시는 분들은 어떻게 코딩을 하고 있나 몇 개를 좀 봤다.
역시 코드를 잘 짜시더라.. 근데 DTO 개념을 사용하시는 분 몇 분 계셨다. DTO가 뭐고, 왜 사용해야할까?

으악 머리아파
DAO는 배울거고 Setter는 DTO하면서 배울거고 Builder패턴은 알고 있고
TDD와 Validtor만 알면 되겠다!
1. DTO
DTO란.. Data Transfer Object. 데이터 전송객체의 약어다. 약어의 경우 풀어야 좀 이해하기 쉽다.
데이터를 전송하는 객체이기 때문에 안에 특별한 비즈니스 로직은 없고 그냥 저장만 할 수 있게 한다.
DTO 이외에도
DAO DB에 접근하는 객체
VO 값 자체를 표현하는 객체
Entity 실제 DB 테이블고 매핑이 되는 클래스
정도가 있는데 DAO나 VO까지는 쓸 일이 없더라도 Entity 정도까지는 알아두면 좋을 것 같다.
MVC 패턴에서는 Controller와 VIEW (유저에게 보여질 화면) 간 정보교환에 사용된다고한다.
로그인 성공하면 code : 200 / message : "success" 등을 반환하는 것도 포함해서
나는 이미 반환형태를 ResponseEntity로 하고 있어서 ResponseEntity의 body에 DTO를 담아서 보내면 된다. ResponseEntity<DTO> 이런 식으로
그냥 그대로 내려보내면 민감한 정보가 유출되거나 (캡슐화 실패) 비즈니스 로직 (Moel) - View 사이에 의존성이 생긴다. 웹이나 자바 개발은 의존성을 줄이는데 목표를 하는 것 같다.
/**
* userEmail : 이메일
* userName : 유저명 (닉네임)
* userNumber : 유저번호 (고유번호)
*/
package com.hongchelin.dto.user;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class userInforDto {
private String userEmail;
private String userName;
private String userNumber;
}
대충 이런 식으로 있다 (예시코드)
Getter와 Setter는 아직 안만들었는데 전에 CRUD 프로젝트 할 때 Setter의 사용을 지양하는게 좋다고하여 그것도 시도해볼 예정이다. 우선 데이터를 저장할 곳을 만들었으니 빼오는 곳이 필요
먼저 userInfo에는 단순히 식별자와 유저권한만 들어가기 때문에 코드를 짜보면
/**
* userEmail : 이메일
* userName : 유저명 (닉네임)
* userNumber : 유저번호 (고유번호)
*/
package com.hongchelin.dto.user;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class MemberDTO {
private String identifier;
private String userRole;
@Builder
public MemberDTO(String identifier, String userRole) {
this.identifier = identifier;
this.userRole = userRole;
}
}
ㅇㅇ
Setter를 안쓰고 Builder 패턴을 사용했다. 그리고 Getter 어노테이션을 통해 getter를 구현하지 않아도 getter를 사용한다. 추가로 저 생성자 역시 Builder 어노테이션을 사용하면 구현하지 않아도 된다. 역시 lombok은 편해
근데 궁금한건 진짜 이렇게하면 되나?
그냥 ResponseEntity의 Builder 방식과 비슷하다
MemberDTO memberdto = MemberDTO.builder()
.identifier("123")
.userRole("inner")
.build();
뭐 이런식으로.. 이러고 ResponseEntity에 넣어서 반환한다.
아직 DB를 활용하지는 않아서 DTO를 많이 활용하지는 않는데 응답코드는 DTO로 반환하기로 했다.
GPT에게 응답코드를 DTO로 반환하는 것에 대한 장점에 대해 물어보니
DTO가 일종의 스펙명세서 역할을 한다, 응답구조의 표준화, 테스트 용이 등이라고하는데
가장 와닿았던 것은 리팩토링이 쉽고 안전하다였다.
이전에 Map으로 형태를 반환할 때 유저 식별자의 String을 'email'에서 'identifier'로 바꾸고, 응답코드에 대한 것도 'code'니, 'status'니 약간 혼재되어 있었는데 모두 같은 DTO를 사용하면 하나도 획일화 시킬 수 있다는 것이다.
아래는 GPT의 답변
✔ 타입 안정성 → 런타임 오류 대신 컴파일 타임에 문제 발견 가능
✔ 자동완성과 문서화 → IDE 지원 + DTO 자체가 스펙 명세서 역할
✔ 유지보수성 향상 → 필드 변경 시 리팩토링이 쉽고 안전
✔ 응답 구조 표준화 → 여러 API에서 일관된 응답 형식 유지
✔ 테스트 용이 → 단위 테스트에서 DTO 객체 그대로 비교 가능
즉, Map/JSON은 빠르게 테스트할 때 편리하지만,
서비스가 커지고 유지보수가 중요해지면 DTO가 거의 필수가 됩니다.
어쨌든 ResponseDTO를 짜는데
응답으로 응답코드와 메시지, redirect-url은 필수로 들어가고, 로그인의 경우에는 엑세스토큰과 리프레시토큰도 새로 발급해주어야한다. 이정도면 되지 않을까 싶다.
package com.hongchelin.dto.user;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class ResponseDTO {
Integer status;
String message;
String redirectUrl;
String accessToken;
String refreshToken;
}
이정도로하고 이제 응답코드를 반환하는 부분에 대해서 Map 형식이 아닌 ResponseDTO 형식으로 바꾸면 된다. Map을 쓸 일이 이제 좀 줄어들듯. 솔직히 Map<String, Object> 치는거 귀찮아
ResponseDTO responseDTO = ResponseDTO.builder()
.status(200)
.message("인증번호 발송에 성공하였습니다.\n"+pwd)
.build();
Map<String, Object> response = new HashMap<String, Object>();
response.put("code", 200);
response.put("message", "인증번호 발송에 성공하였습니다\n" + pwd);
어제 코딩한 인증번호 발송부분에 대한 응답코드 반환 코드
확실히 더 깔끔한 것 같기도 하고? 밑에 기존 코드는 혹시 몰라서 주석처리했다.
추가로 DTO의 요소가 a, b, c 가 있으면 a, b만 작성해도 된다. 단 이때 c는 null로 처리된다.
다 수정했고, 기존 기능들도 문제없이 돌아간다.
DTO에 대해서 몇 자 더 적어보자면...
위에서 null로 반환 될 경우 스프링 yml 파일에
jackson:
default-property-inclusion: non_null
을 추가해주면 null일 경우 반환되지 않는다.
DTO를 파싱하는 방법은 Builder 어노테이션을 사용해서 이미 getter를 사용할 수 있다.
responseDTO.getstatus(); 등의 방식으로 접근. status가 아니라 message에 접근하고 싶으면 getmessage(); 이런 식으로..
근데 boolean 값의 경우에는 get이 아닌 is로 접근해야한다. boolean validity라면 getvalidity(); 가 아니라 isvalidity(); 로 접근해야한다.
그런데 boolean에는 null이 존재하지 않아 위의 jackson 설정을 적용받지 않는다.
이럴 때는 그냥 boolean type이 아니라 Boolean 타입으로 변경해서 가면 된다.
ResponseDTO<ResponseDTO> aaa의 경우에는
a.getBody().get<인자명>(); 등으로 접근하면 끝
단적인 예시를 들어보자면..
어제 내가 만든 JWTFilter에는 checkValidity - getUserRoles로 연쇄적으로 이어지는 메서드, 각각 boolean과 JSON 객체를 반환하는 메서드를 포함해 createToken / createRefreshToken / getTokenFromHeader / checkValidity / getUserRoles / checkvalidityInTrueFalse / getUserRolesInJSON 이 있었는데, checkvalidityInTrueFalse와 getUserRolesInJSON 메서드의 기능을 checkValidity와 getUserRoles 로 합쳤다.
물론 기존의 ResponseEntity<Map<Sting, Object>> 형식으로도 충분히 가능한데.. 왜 DTO 파싱이 더 쉽지? JSON 파싱은 안해봐서 그런가. DTO에서 getter를 기본적으로 지원해줘서 통일된 형식으로 응답값을 반환할 수 있었다.
여기까지하고 다시 DTO의 장점을 돌아보니
- 타입 안정성 -> 이거는 Map의 경우에는 파싱해올 때 인자를 잘못주면 런타임 오류가 생기는데 DTO는 getter를 지원해줘서 컴파일에서 잡아낼 수도 있고, 파싱도 더 쉽고.
- 자동완성과 문서화 -> 자동완성 기능도 좀 봤다.
- 코드 리팩토링이 쉽다 -> 인정. 빌더 패턴으로 getter도 지원해주고 양식도 통일되고
- 응답 구조 표준화 -> 인정
- 테스트 용이 -> 이건 테스트를 안해봐서 모르겠다.
대체적으로 처음에 봤던 장점들이 모두 유효하다.
처음에 DTO 쓸 때는 왜 써야하는지도 모르고 재미도 없었는데, 막상 써보니까 좋잖아? 더 획일적이고, 더 정확하고
지금 웬만한 기능들은 거의 다 ResponseEntity<ResponseDTO> 형식으로 반환하게 해두어서.. 이제 DB 작업 할 때 외엔 DTO 기능도 거진 만든듯하다. 1시간 정도 걸렸나? 1시간 반?
DB에서 유저정보를 긁어올 때 쓸 MemberDTO도 이미 만들어놨고, DB연동하면 이제 본격적으로 DTO를 써보려나
2. 그래서.. 제발 로그인 기능 끝내야지
어제까지는 코드를 발급해서 이메일로 보냈고.. 이제는 코드를 검증하는 시간이다.
발급한 코드를 기억하고 있으려면 DB를 이용하거나 응답코드로 프론트에 보냈다가 다시 백엔드로 돌려받는 방법이 있는데 그건 좀 비추고 DB를 이용해야할 것 같다. Redis라고 적합한게 하나 있는데 우선은 보류. 지금은 인증번호가 오면 맞는지 아닌지만 확인할 예정이다. 왜냐하면 난 피곤하거든

인증번호를 미리 같도록 설정하고 테스트

미리 다르도록 설정하고 테스트
모두 특별히 오류난 곳은 없다.
이제는 Redis 연결만하면 되는데.. 찍먹이라도 해볼까
우선 글 길어지면 재미없으니까 여기까지만
Redis를 할꺼면 다음글로 뵙겠씁니당
그전에
내일 할 거
Redis (안했으면)
투표기능 API 명세서 작성하고 뼈대잡기
'팀 프로젝트 > [2025][GDG]홍대 맛집 아카이빙 프로젝트' 카테고리의 다른 글
| [GDG]홍대 맛집 아카이빙 프로젝트 백엔드 개발 #8 - 투표기능 개발하기 (4) | 2025.07.21 |
|---|---|
| [GDG]홍대 맛집 아카이빙 프로젝트 #7 - 투표기능 기획 (2) | 2025.07.20 |
| [GDG]홍대 맛집 아카이빙 프로젝트 백엔드 개발 #5 - OAuth 최종점검 (0) | 2025.07.18 |
| [GDG] 개발코스 2주차 WIL (1) | 2025.07.17 |
| [GDG]홍대 맛집 아카이빙 프로젝트 백엔드 개발 #4 - 오늘은 OAuth 끝내죠? (7) | 2025.07.17 |