From b373d59710abe5d37e866ec3d799cec2baa50685 Mon Sep 17 00:00:00 2001 From: SEAN Date: Wed, 25 Feb 2026 14:06:54 +0900 Subject: [PATCH] =?UTF-8?q?improvement:=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5/=EA=B2=80=EC=A6=9D=20=EA=B3=84=EC=95=BD=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC=20(#222)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MessageValidateRequestDto에 JsonPropertyName 추가 (snake_case 통일) - MessageValidateRequestDto.Data 타입 string? → object? 변경 - MessageValidationService.ValidateData 파라미터 타입 변경 - Swagger Description 업데이트 (save/validate 엔드포인트) Closes #222 --- SPMS.API/Controllers/MessageController.cs | 4 +- .../DTOs/Message/MessageValidateRequestDto.cs | 9 +++- .../Services/MessageValidationService.cs | 46 +++++++++++++++---- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/SPMS.API/Controllers/MessageController.cs b/SPMS.API/Controllers/MessageController.cs index f0f8579..60f66f4 100644 --- a/SPMS.API/Controllers/MessageController.cs +++ b/SPMS.API/Controllers/MessageController.cs @@ -22,7 +22,7 @@ public class MessageController : ControllerBase } [HttpPost("save")] - [SwaggerOperation(Summary = "메시지 저장", Description = "메시지 템플릿을 저장합니다. 메시지 코드가 자동 생성됩니다.")] + [SwaggerOperation(Summary = "메시지 저장", Description = "메시지 템플릿을 저장합니다. 메시지 코드가 자동 생성됩니다. 필드명은 snake_case(title, body, image_url, link_url, link_type, data)를 사용합니다. 저장 전 validate API로 사전 검증을 권장합니다.")] public async Task SaveAsync([FromBody] MessageSaveRequestDto request) { var serviceId = GetServiceId(); @@ -59,7 +59,7 @@ public class MessageController : ControllerBase } [HttpPost("validate")] - [SwaggerOperation(Summary = "메시지 유효성 검사", Description = "메시지 내용의 유효성을 검사합니다.")] + [SwaggerOperation(Summary = "메시지 유효성 검사", Description = "메시지 내용의 유효성을 검사합니다. save API와 동일한 snake_case 필드명(title, body, image_url, link_url, link_type, data)을 사용합니다. 검증 실패 시 data.errors[]에 field + message 단위로 오류가 반환됩니다.")] public IActionResult ValidateAsync([FromBody] MessageValidateRequestDto request) { var result = _validationService.Validate(request); diff --git a/SPMS.Application/DTOs/Message/MessageValidateRequestDto.cs b/SPMS.Application/DTOs/Message/MessageValidateRequestDto.cs index b5a9fbb..3706716 100644 --- a/SPMS.Application/DTOs/Message/MessageValidateRequestDto.cs +++ b/SPMS.Application/DTOs/Message/MessageValidateRequestDto.cs @@ -1,20 +1,27 @@ using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; namespace SPMS.Application.DTOs.Message; public class MessageValidateRequestDto { [Required] + [JsonPropertyName("title")] public string Title { get; set; } = string.Empty; [Required] + [JsonPropertyName("body")] public string Body { get; set; } = string.Empty; + [JsonPropertyName("image_url")] public string? ImageUrl { get; set; } + [JsonPropertyName("link_url")] public string? LinkUrl { get; set; } + [JsonPropertyName("link_type")] public string? LinkType { get; set; } - public string? Data { get; set; } + [JsonPropertyName("data")] + public object? Data { get; set; } } diff --git a/SPMS.Application/Services/MessageValidationService.cs b/SPMS.Application/Services/MessageValidationService.cs index bb2f513..ca86bb6 100644 --- a/SPMS.Application/Services/MessageValidationService.cs +++ b/SPMS.Application/Services/MessageValidationService.cs @@ -82,27 +82,55 @@ public class MessageValidationService : IMessageValidationService errors.Add(new FieldError { Field = "link_type", Message = "link_type은 deeplink, web, none 중 하나여야 합니다." }); } - private static void ValidateData(string? data, List errors) + private static void ValidateData(object? data, List errors) { - if (string.IsNullOrWhiteSpace(data)) + if (data is null) return; - try + // object? → JSON 문자열로 변환 + string jsonString; + if (data is JsonElement jsonElement) { - using var doc = JsonDocument.Parse(data); - if (doc.RootElement.ValueKind != JsonValueKind.Object) + if (jsonElement.ValueKind == JsonValueKind.Null || jsonElement.ValueKind == JsonValueKind.Undefined) + return; + + if (jsonElement.ValueKind != JsonValueKind.Object) { errors.Add(new FieldError { Field = "data", Message = "data는 JSON 객체여야 합니다." }); return; } + + jsonString = jsonElement.GetRawText(); } - catch (JsonException) + else if (data is string strData) { - errors.Add(new FieldError { Field = "data", Message = "유효한 JSON 형식이 아닙니다." }); - return; + if (string.IsNullOrWhiteSpace(strData)) + return; + + try + { + using var doc = JsonDocument.Parse(strData); + if (doc.RootElement.ValueKind != JsonValueKind.Object) + { + errors.Add(new FieldError { Field = "data", Message = "data는 JSON 객체여야 합니다." }); + return; + } + } + catch (JsonException) + { + errors.Add(new FieldError { Field = "data", Message = "유효한 JSON 형식이 아닙니다." }); + return; + } + + jsonString = strData; + } + else + { + // 기타 타입은 직렬화하여 검증 + jsonString = JsonSerializer.Serialize(data); } - if (System.Text.Encoding.UTF8.GetByteCount(data) > MaxDataSizeBytes) + if (System.Text.Encoding.UTF8.GetByteCount(jsonString) > MaxDataSizeBytes) errors.Add(new FieldError { Field = "data", Message = $"data는 {MaxDataSizeBytes / 1024}KB를 초과할 수 없습니다." }); } } -- 2.45.1