D revisó este gist . Ir a la revisión
1 file changed, 224 insertions
nti설계.md(archivo creado)
| @@ -0,0 +1,224 @@ | |||
| 1 | + | 지금 구조는 **1:N 관계 (알림 1건 ↔ 처리내역 여러 건)** 이므로, | |
| 2 | + | 이건 단순 “속성”이 아니라 **하위 리소스(sub-resource)** 로 취급해야 합니다. | |
| 3 | + | ||
| 4 | + | 즉, “NTI(알림)”가 상위 도메인, “처리내역”은 NTI 하위에 종속된 도메인으로 보아야 합니다. | |
| 5 | + | 아래는 **REST 설계, DTO 분리, 서비스 구조** 모두를 기준으로 한 권장 설계안입니다. | |
| 6 | + | ||
| 7 | + | --- | |
| 8 | + | ||
| 9 | + | ## 1. 도메인 관계 요약 | |
| 10 | + | ||
| 11 | + | | 개체 | 설명 | 관계 | | |
| 12 | + | | ------------ | ----------- | ------ | | |
| 13 | + | | `Nti` | 알림 (상위 도메인) | 1 | | |
| 14 | + | | `NtiProcess` | 알림의 처리내역 | N (하위) | | |
| 15 | + | ||
| 16 | + | 예시: | |
| 17 | + | ||
| 18 | + | ``` | |
| 19 | + | Nti(id=100, title="재난 문자") | |
| 20 | + | ├── NtiProcess(id=1, status="발송대기", updatedAt="2025-10-31") | |
| 21 | + | ├── NtiProcess(id=2, status="발송완료", updatedAt="2025-10-31") | |
| 22 | + | └── ... | |
| 23 | + | ``` | |
| 24 | + | ||
| 25 | + | --- | |
| 26 | + | ||
| 27 | + | ## 2. REST API 설계 | |
| 28 | + | ||
| 29 | + | ### (1) 상위 도메인: 알림 | |
| 30 | + | ||
| 31 | + | | 기능 | Method | URI | 설명 | | |
| 32 | + | | -------- | ------ | --------------------- | -------- | | |
| 33 | + | | 알림 목록 조회 | `GET` | `/api/v1/nti` | 알림 전체 목록 | | |
| 34 | + | | 알림 상세 조회 | `GET` | `/api/v1/nti/{ntiId}` | 알림 단건 조회 | | |
| 35 | + | | 알림 생성 | `POST` | `/api/v1/nti` | 새 알림 생성 | | |
| 36 | + | ||
| 37 | + | ### (2) 하위 리소스: 알림의 처리내역 | |
| 38 | + | ||
| 39 | + | | 기능 | Method | URI | 설명 | | |
| 40 | + | | ----------------- | ------ | ------------------------------------------- | --------------- | | |
| 41 | + | | 특정 알림의 처리내역 목록 조회 | `GET` | `/api/v1/nti/{ntiId}/processes` | 특정 알림의 처리내역 리스트 | | |
| 42 | + | | 특정 처리내역 조회 | `GET` | `/api/v1/nti/{ntiId}/processes/{processId}` | 특정 내역 상세 | | |
| 43 | + | | 처리내역 추가 | `POST` | `/api/v1/nti/{ntiId}/processes` | 해당 알림에 내역 추가 | | |
| 44 | + | ||
| 45 | + | --- | |
| 46 | + | ||
| 47 | + | ## 3. Controller 구조 | |
| 48 | + | ||
| 49 | + | ```java | |
| 50 | + | @RestController | |
| 51 | + | @RequestMapping("/api/v1/nti") | |
| 52 | + | @RequiredArgsConstructor | |
| 53 | + | @Slf4j | |
| 54 | + | public class NtiController { | |
| 55 | + | ||
| 56 | + | private final NtiService ntiService; | |
| 57 | + | ||
| 58 | + | @GetMapping | |
| 59 | + | public ResponseEntity<List<NtiListResponse>> getAllNti() { | |
| 60 | + | return ResponseEntity.ok(ntiService.getAll()); | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | @GetMapping("/{ntiId}") | |
| 64 | + | public ResponseEntity<NtiDetailDto> getNti(@PathVariable Long ntiId) { | |
| 65 | + | return ResponseEntity.ok(ntiService.getById(ntiId)); | |
| 66 | + | } | |
| 67 | + | } | |
| 68 | + | ``` | |
| 69 | + | ||
| 70 | + | ```java | |
| 71 | + | @RestController | |
| 72 | + | @RequestMapping("/api/v1/nti/{ntiId}/processes") | |
| 73 | + | @RequiredArgsConstructor | |
| 74 | + | @Slf4j | |
| 75 | + | public class NtiProcessController { | |
| 76 | + | ||
| 77 | + | private final NtiProcessService ntiProcessService; | |
| 78 | + | ||
| 79 | + | @GetMapping | |
| 80 | + | public ResponseEntity<List<NtiProcessDto>> getProcesses(@PathVariable Long ntiId) { | |
| 81 | + | return ResponseEntity.ok(ntiProcessService.getProcessesByNtiId(ntiId)); | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | @GetMapping("/{processId}") | |
| 85 | + | public ResponseEntity<NtiProcessDto> getProcess( | |
| 86 | + | @PathVariable Long ntiId, | |
| 87 | + | @PathVariable Long processId | |
| 88 | + | ) { | |
| 89 | + | return ResponseEntity.ok(ntiProcessService.getProcessDetail(ntiId, processId)); | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | @PostMapping | |
| 93 | + | public ResponseEntity<Void> addProcess( | |
| 94 | + | @PathVariable Long ntiId, | |
| 95 | + | @RequestBody NtiProcessRequest request | |
| 96 | + | ) { | |
| 97 | + | ntiProcessService.addProcess(ntiId, request); | |
| 98 | + | return ResponseEntity.status(HttpStatus.CREATED).build(); | |
| 99 | + | } | |
| 100 | + | } | |
| 101 | + | ``` | |
| 102 | + | ||
| 103 | + | --- | |
| 104 | + | ||
| 105 | + | ## 4. DTO 구조 | |
| 106 | + | ||
| 107 | + | ### Nti (상위) | |
| 108 | + | ||
| 109 | + | ```java | |
| 110 | + | public class NtiDetailDto { | |
| 111 | + | private Long id; | |
| 112 | + | private String title; | |
| 113 | + | private String classification; | |
| 114 | + | private LocalDateTime createdAt; | |
| 115 | + | private List<NtiProcessDto> processes; // 옵션: 포함 조회용 | |
| 116 | + | } | |
| 117 | + | ``` | |
| 118 | + | ||
| 119 | + | ### NtiProcess (하위) | |
| 120 | + | ||
| 121 | + | ```java | |
| 122 | + | public class NtiProcessDto { | |
| 123 | + | private Long id; | |
| 124 | + | private String status; | |
| 125 | + | private String handler; | |
| 126 | + | private String comment; | |
| 127 | + | private LocalDateTime updatedAt; | |
| 128 | + | } | |
| 129 | + | ``` | |
| 130 | + | ||
| 131 | + | 요청용 DTO | |
| 132 | + | ||
| 133 | + | ```java | |
| 134 | + | public class NtiProcessRequest { | |
| 135 | + | private String status; | |
| 136 | + | private String handler; | |
| 137 | + | private String comment; | |
| 138 | + | } | |
| 139 | + | ``` | |
| 140 | + | ||
| 141 | + | --- | |
| 142 | + | ||
| 143 | + | ## 5. 서비스 및 패키지 구조 | |
| 144 | + | ||
| 145 | + | ``` | |
| 146 | + | com.example.nti | |
| 147 | + | ├── controller | |
| 148 | + | │ ├── NtiController.java | |
| 149 | + | │ └── NtiProcessController.java | |
| 150 | + | ├── service | |
| 151 | + | │ ├── NtiService.java | |
| 152 | + | │ └── NtiProcessService.java | |
| 153 | + | ├── dto | |
| 154 | + | │ ├── NtiListResponse.java | |
| 155 | + | │ ├── NtiDetailDto.java | |
| 156 | + | │ ├── NtiProcessDto.java | |
| 157 | + | │ └── NtiProcessRequest.java | |
| 158 | + | ├── repository | |
| 159 | + | │ ├── NtiRepository.java | |
| 160 | + | │ └── NtiProcessRepository.java | |
| 161 | + | └── entity | |
| 162 | + | ├── Nti.java | |
| 163 | + | └── NtiProcess.java | |
| 164 | + | ``` | |
| 165 | + | ||
| 166 | + | --- | |
| 167 | + | ||
| 168 | + | ## 6. 데이터 모델 예시 (JPA) | |
| 169 | + | ||
| 170 | + | ```java | |
| 171 | + | @Entity | |
| 172 | + | public class Nti { | |
| 173 | + | @Id @GeneratedValue | |
| 174 | + | private Long id; | |
| 175 | + | ||
| 176 | + | private String title; | |
| 177 | + | private String classification; | |
| 178 | + | ||
| 179 | + | @OneToMany(mappedBy = "nti", cascade = CascadeType.ALL, orphanRemoval = true) | |
| 180 | + | private List<NtiProcess> processes = new ArrayList<>(); | |
| 181 | + | } | |
| 182 | + | ``` | |
| 183 | + | ||
| 184 | + | ```java | |
| 185 | + | @Entity | |
| 186 | + | public class NtiProcess { | |
| 187 | + | @Id @GeneratedValue | |
| 188 | + | private Long id; | |
| 189 | + | ||
| 190 | + | @ManyToOne(fetch = FetchType.LAZY) | |
| 191 | + | @JoinColumn(name = "nti_id") | |
| 192 | + | private Nti nti; | |
| 193 | + | ||
| 194 | + | private String status; | |
| 195 | + | private String handler; | |
| 196 | + | private String comment; | |
| 197 | + | private LocalDateTime updatedAt; | |
| 198 | + | } | |
| 199 | + | ``` | |
| 200 | + | ||
| 201 | + | --- | |
| 202 | + | ||
| 203 | + | ## 7. 설계 요약 | |
| 204 | + | ||
| 205 | + | | 구분 | 설계 방식 | URI 예시 | | |
| 206 | + | | ---------- | ------------------------------------ | ------------------------------------------- | | |
| 207 | + | | 상위 리소스 | NTI (알림) | `/api/v1/nti/{ntiId}` | | |
| 208 | + | | 하위 리소스 | 처리내역 | `/api/v1/nti/{ntiId}/processes` | | |
| 209 | + | | 단건 처리내역 조회 | ID 기반 | `/api/v1/nti/{ntiId}/processes/{processId}` | | |
| 210 | + | | DTO | `NtiProcessDto`, `NtiProcessRequest` | 속성 분리 | | |
| 211 | + | ||
| 212 | + | --- | |
| 213 | + | ||
| 214 | + | ## 8. 핵심 요약 문장 | |
| 215 | + | ||
| 216 | + | > 알림 하위의 처리내역은 `Nti`의 속성이 아니라 `NtiProcess`라는 독립된 **하위 리소스(Sub-resource)** 로 분리해야 하며, | |
| 217 | + | > `/api/v1/nti/{ntiId}/processes` 형태로 접근하도록 설계하는 것이 RESTful하고 확장성에 유리합니다. | |
| 218 | + | ||
| 219 | + | --- | |
| 220 | + | ||
| 221 | + | 원하신다면 | |
| 222 | + | “처리내역이 단순 로그 형태인지, 상태 전이(예: 진행중→완료)인지” 알려주시면 | |
| 223 | + | 그 흐름에 맞는 **상태 기반 도메인 설계(state transition model)** 까지 구체화해드릴 수 있습니다. | |
| 224 | + | 그 부분 포함해서 볼까요? | |
Siguiente
Anterior