지금 구조는 **1:N 관계 (알림 1건 ↔ 처리내역 여러 건)** 이므로, 이건 단순 “속성”이 아니라 **하위 리소스(sub-resource)** 로 취급해야 합니다. 즉, “NTI(알림)”가 상위 도메인, “처리내역”은 NTI 하위에 종속된 도메인으로 보아야 합니다. 아래는 **REST 설계, DTO 분리, 서비스 구조** 모두를 기준으로 한 권장 설계안입니다. --- ## 1. 도메인 관계 요약 | 개체 | 설명 | 관계 | | ------------ | ----------- | ------ | | `Nti` | 알림 (상위 도메인) | 1 | | `NtiProcess` | 알림의 처리내역 | N (하위) | 예시: ``` Nti(id=100, title="재난 문자") ├── NtiProcess(id=1, status="발송대기", updatedAt="2025-10-31") ├── NtiProcess(id=2, status="발송완료", updatedAt="2025-10-31") └── ... ``` --- ## 2. REST API 설계 ### (1) 상위 도메인: 알림 | 기능 | Method | URI | 설명 | | -------- | ------ | --------------------- | -------- | | 알림 목록 조회 | `GET` | `/api/v1/nti` | 알림 전체 목록 | | 알림 상세 조회 | `GET` | `/api/v1/nti/{ntiId}` | 알림 단건 조회 | | 알림 생성 | `POST` | `/api/v1/nti` | 새 알림 생성 | ### (2) 하위 리소스: 알림의 처리내역 | 기능 | Method | URI | 설명 | | ----------------- | ------ | ------------------------------------------- | --------------- | | 특정 알림의 처리내역 목록 조회 | `GET` | `/api/v1/nti/{ntiId}/processes` | 특정 알림의 처리내역 리스트 | | 특정 처리내역 조회 | `GET` | `/api/v1/nti/{ntiId}/processes/{processId}` | 특정 내역 상세 | | 처리내역 추가 | `POST` | `/api/v1/nti/{ntiId}/processes` | 해당 알림에 내역 추가 | --- ## 3. Controller 구조 ```java @RestController @RequestMapping("/api/v1/nti") @RequiredArgsConstructor @Slf4j public class NtiController { private final NtiService ntiService; @GetMapping public ResponseEntity> getAllNti() { return ResponseEntity.ok(ntiService.getAll()); } @GetMapping("/{ntiId}") public ResponseEntity getNti(@PathVariable Long ntiId) { return ResponseEntity.ok(ntiService.getById(ntiId)); } } ``` ```java @RestController @RequestMapping("/api/v1/nti/{ntiId}/processes") @RequiredArgsConstructor @Slf4j public class NtiProcessController { private final NtiProcessService ntiProcessService; @GetMapping public ResponseEntity> getProcesses(@PathVariable Long ntiId) { return ResponseEntity.ok(ntiProcessService.getProcessesByNtiId(ntiId)); } @GetMapping("/{processId}") public ResponseEntity getProcess( @PathVariable Long ntiId, @PathVariable Long processId ) { return ResponseEntity.ok(ntiProcessService.getProcessDetail(ntiId, processId)); } @PostMapping public ResponseEntity addProcess( @PathVariable Long ntiId, @RequestBody NtiProcessRequest request ) { ntiProcessService.addProcess(ntiId, request); return ResponseEntity.status(HttpStatus.CREATED).build(); } } ``` --- ## 4. DTO 구조 ### Nti (상위) ```java public class NtiDetailDto { private Long id; private String title; private String classification; private LocalDateTime createdAt; private List processes; // 옵션: 포함 조회용 } ``` ### NtiProcess (하위) ```java public class NtiProcessDto { private Long id; private String status; private String handler; private String comment; private LocalDateTime updatedAt; } ``` 요청용 DTO ```java public class NtiProcessRequest { private String status; private String handler; private String comment; } ``` --- ## 5. 서비스 및 패키지 구조 ``` com.example.nti ├── controller │ ├── NtiController.java │ └── NtiProcessController.java ├── service │ ├── NtiService.java │ └── NtiProcessService.java ├── dto │ ├── NtiListResponse.java │ ├── NtiDetailDto.java │ ├── NtiProcessDto.java │ └── NtiProcessRequest.java ├── repository │ ├── NtiRepository.java │ └── NtiProcessRepository.java └── entity ├── Nti.java └── NtiProcess.java ``` --- ## 6. 데이터 모델 예시 (JPA) ```java @Entity public class Nti { @Id @GeneratedValue private Long id; private String title; private String classification; @OneToMany(mappedBy = "nti", cascade = CascadeType.ALL, orphanRemoval = true) private List processes = new ArrayList<>(); } ``` ```java @Entity public class NtiProcess { @Id @GeneratedValue private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "nti_id") private Nti nti; private String status; private String handler; private String comment; private LocalDateTime updatedAt; } ``` --- ## 7. 설계 요약 | 구분 | 설계 방식 | URI 예시 | | ---------- | ------------------------------------ | ------------------------------------------- | | 상위 리소스 | NTI (알림) | `/api/v1/nti/{ntiId}` | | 하위 리소스 | 처리내역 | `/api/v1/nti/{ntiId}/processes` | | 단건 처리내역 조회 | ID 기반 | `/api/v1/nti/{ntiId}/processes/{processId}` | | DTO | `NtiProcessDto`, `NtiProcessRequest` | 속성 분리 | --- ## 8. 핵심 요약 문장 > 알림 하위의 처리내역은 `Nti`의 속성이 아니라 `NtiProcess`라는 독립된 **하위 리소스(Sub-resource)** 로 분리해야 하며, > `/api/v1/nti/{ntiId}/processes` 형태로 접근하도록 설계하는 것이 RESTful하고 확장성에 유리합니다. --- 원하신다면 “처리내역이 단순 로그 형태인지, 상태 전이(예: 진행중→완료)인지” 알려주시면 그 흐름에 맞는 **상태 기반 도메인 설계(state transition model)** 까지 구체화해드릴 수 있습니다. 그 부분 포함해서 볼까요?