DTO 관리에 λŒ€ν•œ κ³ λ―Όκ³Ό λ¦¬νŽ™ν† λ§

πŸ€” 1. κ³ λ―Ό..

ν˜„μž¬ λ‚˜λŠ” μ°½μ—… ν”„λ‘œμ νŠΈμ—μ„œ 혼자 λ°±μ—”λ“œ κ°œλ°œμ„ 맑아 μ§„ν–‰ν•˜κ³  μžˆλ‹€. 

 

λŒ€ν•™μƒμ΄λ‹ˆ λ‹Ήμ—°νžˆ μ‚¬μˆ˜λ„ μ—†κ³ , 같이 κ³ λ―Όν•˜κ³  ν† λ‘ ν•  λ°±μ—”λ“œ νŒ€μ›λ„ μ—†μœΌλ‹ˆ λ‚΄κ°€ κ²°μ •ν•œ λͺ¨λ“  것이 κ·ΈλŒ€λ‘œ ν”„λ‘œμ νŠΈμ— λ°˜μ˜λ˜μ—ˆλ‹€. λ‚˜μ˜ 잘λͺ»λœ νŒλ‹¨, μŠ΅κ΄€ λ“€λ‘œ ν”„λ‘œμ νŠΈκ°€ 망가지지 μ•Šλ„λ‘ ν•˜κΈ° μœ„ν•΄ λŠμž„μ—†μ΄ λ˜λŒμ•„λ³΄κ³  쑰언을 κ΅¬ν•˜λ©° μ§„ν–‰ν•˜κ³  μžˆλ‹€. 

 

ν˜„μž¬ ν”„λ‘œμ νŠΈλŠ” 큰 ν‹€λ‘œ domain, global λ₯Ό λ‚˜λˆ΄κ³ , 도메인 λ³„λ‘œ entity, dtoλ“± μƒμ„±ν•˜κ³  μžˆμ—ˆλ‹€. 

ν•˜μ§€λ§Œ ν”„λ‘œμ νŠΈ 규λͺ¨κ°€ 컀짐에 따라 dto관리λ₯Ό ν•˜κΈ°κ°€ λ„ˆλ¬΄ νž˜λ“€μ—ˆλ‹€. λ‹Ήμž₯ λͺ‡ μ£Ό 전에 κ°œλ°œν–ˆλ˜ μ½”λ“œλ₯Ό λ”± 봀을 λ•Œ 무슨 역할을 ν•˜λŠ” ν΄λž˜μŠ€μΈμ§€ λ°”λ‘œ μ•Œμ•„μ°¨λ¦¬κΈ° νž˜λ“€μ—ˆλ‹€. λ‚˜λ¦„ μ½”λ“œμ— 신경을 많이 μ¨μ„œ 개발 ν•˜λŠ” μŠ€νƒ€μΌμΈλ° 규λͺ¨κ°€ μ»€μ§€λ‹ˆ μ–΄μ§€λŸ¬μ› λ‹€. 

 

μ•„λž˜λŠ” ν”„λ‘œμ νŠΈ 디렉토리 쀑 일뢀λ₯Ό μΊ‘μ³ν•œ 사진이닀.

 

dtos 폴더λ₯Ό 보면 offer, project λ“± domain 폴더가 또 λ“€μ–΄μžˆλ‹€. 

λ§Œμ•½ offer 도메인과 κ΄€λ ¨λœ λ°˜ν™˜ 데이터에 jobPost 데이터가 ν¬ν•¨λ˜μ•Ό ν•œλ‹€λ©΄ offer 디렉토리에 dtoλ₯Ό μƒμ„±ν•΄μ„œ μ‚¬μš©ν–ˆμ—ˆλ‹€. 

λ‚˜λ¦„ μ•Œμ•„λ³΄κΈ° μ‰½μžκ³  μ΄λ ‡κ²Œ ν–ˆλŠ”λ° 쒋은 방식이 μ•„λ‹ˆλž€ 생각이 λ“€μ—ˆλ‹€. 

 

μ—­μ‹œ λ‹€λ₯Έ λ§Žμ€ 개발자 뢄듀이 이미 이런 고민을 ν–ˆκ³ , λ‹€μ–‘ν•œ λ°©λ²•μœΌλ‘œ ν•΄κ²°ν•˜κ³  μžˆμ—ˆλ‹€. 

 

 

κ·Έ 쀑 Inner Class둜 λ¦¬νŽ™ν„°λ§μ„ 진행할 μ˜ˆμ •μ΄λ‹€. 

 

사싀 μ˜ˆμ „ ν”„λ‘œμ νŠΈμ—λ„ 이걸 λ„μž…ν• κΉŒ ν•˜λ‹€κ°€ [outer_class].[inner_class]  처럼 μ ‘κ·Όν•΄μ•Ό ν•΄μ„œ λ„μž…ν•˜μ§€ μ•Šμ•˜λŠ”λ°, 그땐 ν”„λ‘œμ νŠΈ 규λͺ¨κ°€ μž‘μ•„μ„œ 그런 생각을 ν–ˆλ˜ 것 κ°™λ‹€.. 

 

 

λ¬Όλ‘  dtoλ₯Ό μ‚¬μš©ν•  λ•Œ 이전보닀 κΈΈμ–΄μ§€λŠ” 건 λ§žμ§€λ§Œ λͺ¨λ“  ν•΄κ²°μ±…μ—” trade-off κ°€ μžˆλŠ” λ²•μ΄λ‹ˆ..^^

No Silver Bullet (μ€μ΄ν™œμ€ μ—†λ‹€)

 

 

 

 

 

 

2. κ°œμ„  방법

DTO 관리 방법을 μ°Ύμ•„λ³΄λ‹€λ³΄λ‹ˆ Inner Classλ₯Ό ν™œμš©ν•˜λŠ” 방법이 μžˆμ–΄μ„œ ν”„λ‘œμ νŠΈμ— λ„μž…ν–ˆλ‹€.

 

 

Inner Classλ₯Ό ν™œμš©ν•˜λ©΄ μ—¬λŸ¬κ°œμ˜ DTO ν•˜λ‚˜μ˜ ν΄λž˜μŠ€μ—μ„œ κΉ”λ”ν•˜κ²Œ 관리할 수 μžˆλ‹€. μˆ˜μ •μ‚¬ν•­μ΄ 생기면 μ—¬λŸ¬ 곳을 λŒμ•„λ‹€λ‹ˆλ©΄μ„œ μˆ˜μ •ν•  ν•„μš”κ°€ μ—†κ³  μœ„μ—μ„œ λ‚΄κ°€ 느꼈던 λ¬Έμ œμ μ„ ν•΄κ²°ν•  수 μžˆμ—ˆλ‹€.

 

 

μ•„λž˜λŠ” Inner Class λ₯Ό μ‚¬μš©ν•˜μ—¬ κ³„μ„ ν•˜λŠ” μ½”λ“œμ˜ μ˜ˆμ‹œμ΄λ‹€. 

 

2.1 κ°œμ„  μ „

 

- 메인 λ°˜ν™˜ DTO

public class ProductDto {

    private final SellerDto seller;
    private final CategoryDto category;
    private final AddressDto address;
    private final String title;
    private final String contents;
    
    }

 

 

- ν•„λ“œμ— λ“€μ–΄κ°ˆ DTO

public class SellerDto {

    // μƒλž΅...
    
    }
    
public class CategoryDto {

    // μƒλž΅...
    
    }
    
public class AddressDto {

    // μƒλž΅...
    
    }

 

λ‚˜μ˜ ν”„λ‘œμ νŠΈ κ΅¬μ‘°μ˜€λ‹€λ©΄ SellerDtoλŠ” Seller 디렉토리에, CategoryDtoλŠ” CategoryDto에.. μ–΄μ§€λŸ½λ‹€..

이걸 InnerClass둜 κ΄€λ¦¬ν•˜λ©΄ μ•„λž˜μ™€ 같이 μ‚¬μš©ν•  수 μžˆλ‹€. 

 

 

2.2 κ°œμ„  ν›„

public class ProductDto {

    private final String title;
    private final String contents;
    private final SellerDto seller;
    private final CategoryDto category;
    private final AddressDto address;

    
    private static class SellerDto {
	// μƒλž΅
    }

    private static class CategoryDto {
	// μƒλž΅
    }

    private static class AddressDto {
	// μƒλž΅
    }
}

 

ν™•μ‹€νžˆ μ–΄λ–€ Dtoλ₯Ό 보고 어디에 μ‚¬μš©ν•˜λŠ” Dto인지 μ•Œμ•„μ±„κΈ° μ‰¬μšΈ 것 κ°™λ‹€.

 

 

μ•„λž˜λŠ” μ‹€μ œλ‘œ λ‚΄ μ½”λ“œλ₯Ό μ–΄λ–»κ²Œ λ¦¬νŽ™ν„°λ§ ν–ˆλŠ”μ§€ 일뢀 과정을 λ‹΄μ•˜λ‹€. 

 

μ•„λž˜ DTOλŠ” 인λ ₯ 관리 κΈ°λŠ₯μ—μ„œ λ°˜ν™˜λ˜λŠ” DTO이닀. 

 

 

dtoλͺ…이 정말.. λ³„λ‘œλ‹€. μ²˜μŒλΆ€ν„° μ €λ ‡κ²Œ μ§€μ €λΆ„ν•˜μ§„ μ•Šμ•˜λ‹€. μ–΄λ–€ dtoλ₯Ό 봀을 λ•Œ μ–΄λ””μ„œ μ‚¬μš©ν•˜λŠ” dto인지 클래슀λͺ…λ§Œ 보고도 μ•Œ 수 있게 ν•˜κ³ μ‹Άμ–΄ μ €λŸ°μ‹μœΌλ‘œ μž‘μ„±ν–ˆλŠ”λ° λ„ˆλ¬΄ λ§Žμ•„μ§€λ‹€λ³΄λ‹ˆ 맀번 λ‹€λ₯Έ μ΄λ¦„μœΌλ‘œ μž‘μ„±ν•˜λŠ”κ²Œ νž˜λ“€μ–΄μ§€κ³  κΈΈμ–΄μ‘Œλ‹€. 

 

MemberResponseForApplyHistory ν΄λž˜μŠ€λŠ” member 디렉토리에 λ“€μ–΄κ°€μžˆμ—ˆκ³ , 이것도 member λ””λ ‰ν† λ¦¬μ—μ„œ ν•΄λ‹Ή dtoλ₯Ό 봀을 λ•Œ 어디에 μ‚¬μš©ν•˜λŠ”κ±΄μ§€ μ•Œ 수 있게 ν•˜λ €κ³  μ €λŸ°μ‹μœΌλ‘œ μ§€μ—ˆλ‹€.

 

 

이걸 inner class둜 μ‚¬μš©ν•˜λ©΄μ„œ μ•„λž˜μ™€ 같이 λ³€κ²½ν•  수 μžˆμ—ˆλ‹€. 

member 디렉토리에 있던 dtoλŠ” μ‚­μ œν•˜κ³  static class둜 μˆ˜μ •ν•΄μ€¬λ‹€. 

 

 

@Getter
@Builder
public class ApplyManageResponse {
    /**
     * 인λ ₯ 관리: λŒ€κΈ° 쀑인 인뢀 쑰회
     * 인λ ₯ 관리: ν™•μ • 된 인뢀 쑰회
     */
    private Long applyId;
    private MemberResponse memberResponse;

    public static ApplyManageResponse from(Apply apply) {
        return ApplyManageResponse.builder()
                .applyId(apply.getId())
                .memberResponse(MemberResponse.from(apply.getMember()))
                .build();
    }


    @Builder
    public static class MemberResponse {
        private Long memberId;
        private String workerName; // λ…Έλ™μž 이름
        private String phone; // νœ΄λŒ€ν° 번호
        private Integer age; // λ‚˜μ΄
        private Gender gender; // 성별
        private String nationality; // ꡭ적
        private Integer workTimes; // μΆœμ—­ 횟수
        private Float participationRate; // μ°Έμ—¬μœ¨

        public static MemberResponse from(Member member) {
            // λ‚˜μ΄ 계산
            int age = AgeTransfer.getAgeByBirth(member.getWorkerInfo().getBrith());

            // μΆœμ—­ 횟수, μ°Έμ—¬μœ¨
            List<History> workHistory = member.getHistoryList().stream()
                    .filter(history -> history.getEndStatus() == WorkStatus.FINISH_WORK)
                    .collect(Collectors.toList());
            int workTimes = workHistory.size();
            float participationRate = (float) workTimes / (float) member.getHistoryList().size();

            // μΆœμ—­ 내역이 없을 경우 -1
            if (workTimes == 0) {
                participationRate = -1;
            }

            return MemberResponse.builder()
                    .memberId(member.getId())
                    .workerName(member.getWorkerInfo().getWorkerName())
                    .phone(member.getPhone())
                    .age(age)
                    .gender(member.getWorkerInfo().getGender())
                    .nationality(member.getWorkerInfo().getNationality())
                    .workTimes(workTimes)
                    .participationRate(participationRate)
                    .build();
        }
    }
}

 

μœ„ μ½”λ“œμ—μ„  ν•˜λ‚˜μ˜ dto만 inner둜 λ‹΄μ•˜μ§€λ§Œ 4~5개의 dtoλ₯Ό λž©ν•‘ν•˜λŠ” dto도 μ‘΄μž¬ν–ˆκ³  Inner Class둜 κΉ”λ”ν•˜κ²Œ μ²˜λ¦¬ν•  수 μžˆμ—ˆλ‹€. 

 

 

 

 

 

 

3. κ²°λ‘ 

μˆ˜μ •ν•  λ‚΄μš©λ“€μ΄ λ§Žμ•„ μˆ˜μ •μ„ μ™„λ£Œν•œκ±΄ μ•„λ‹ˆμ§€λ§Œ ν™•μ‹€ν•œκ±΄ μƒˆλ‘œμš΄ λ°±μ—”λ“œ κ°œλ°œμžκ°€ ν•©λ₯˜ν•˜κ²Œ 됐을 λ•Œ μ½”λ“œλ₯Ό μ΄ν•΄ν•˜κΈ° 쉽겠닀 λΌλŠ” 생각이 λ“€μ—ˆλ‹€. 

 

 

이 λ°©μ‹μ˜ ν•œκ°€μ§€ μ•„μ‰¬μš΄ 점이 μžˆλ‹€λ©΄ μ—¬λŸ¬ κ³³μ—μ„œ λ˜‘κ°™μ€ ν˜•νƒœλ‘œ μ‚¬μš©ν•˜λŠ” DTOκ°€ Inner Class둜 μ‘΄μž¬ν•œλ‹€λ©΄ μž¬μ‚¬μš©ν•˜κΈ° μ• λ§€ν•œ 감이 μžˆλ‹€.

이럴 경우 λ‚˜λŠ” Inner Class둜 두지 μ•Šκ³  κ·Έλƒ₯ 곡용 DTO둜 λΆ„λ¦¬ν•΄μ„œ μ‚¬μš©ν•˜κ³  μžˆλ‹€.

 

 

더 쒋은 방법이 μžˆλ‹€λ©΄ λŒ“κΈ€λ‘œ λ‚¨κ²¨μ£Όμ„Έμš”!