spring

[Spring & Vue.js] ๊ณตํ†ต ์ฝ”๋“œ ๊ทธ๋ฃน ํ•œ ๋ฒˆ์— ์š”์ฒญํ•˜๊ณ  ๋ฌถ์–ด์„œ ์‘๋‹ต๋ฐ›๊ธฐ (์กฐํšŒ API ์„ค๊ณ„)

devJK93 2025. 5. 1.

๐Ÿ“ ๊ฐœ์š”

๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ์ ํŠธ์—์„œ "๊ณตํ†ต ์ฝ”๋“œ"๋Š” ํ•„์ˆ˜๋‹ค.

ํŠนํžˆ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ๋“œ๋กญ๋‹ค์šด, ์…€๋ ‰ํŠธ๋ฐ•์Šค ๋“ฑ์„ ๊ทธ๋ฆด ๋•Œ ๋‹ค์–‘ํ•œ ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

Vue.js์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ฝ”๋“œ ๊ทธ๋ฃน์„ ํ•œ ๋ฒˆ์— ์š”์ฒญํ•˜๊ณ , Spring ๋ฐฑ์—”๋“œ์—์„œ ๊ทธ๋ฃน๋ณ„๋กœ ์ฝ”๋“œ๋“ค์„ ๋ฌถ์–ด ์‘๋‹ตํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ์„ค๊ณ„ํ•ด๋ณด์ž.


๐Ÿ’ก ๋ชฉํ‘œ

ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์ด๋Ÿฐ ์‹์œผ๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด:

["position", "skill_level", "team"]

 

๋ฐฑ์—”๋“œ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ทธ๋ฃน๋ณ„๋กœ ์ •๋ฆฌ๋œ ์‘๋‹ต์„ ๋ฆฌํ„ดํ•œ๋‹ค:

[
  {
    "groupCode": "position",
    "codes": [
      { "code": "dev", "name": "๊ฐœ๋ฐœ์ž" },
      { "code": "design", "name": "๋””์ž์ด๋„ˆ" }
    ]
  },
  {
    "groupCode": "skill_level",
    "codes": [
      { "code": "senior", "name": "๊ณ ๊ธ‰" },
      { "code": "junior", "name": "์ดˆ๊ธ‰" }
    ]
  }
]

๐Ÿ—‚๏ธ DB ํ…Œ์ด๋ธ” ๊ตฌ์กฐ ์˜ˆ์‹œ

์šฐ๋ฆฌ๋Š” TB_CMMN_CODE ํ…Œ์ด๋ธ”์„ ๊ธฐ์ค€์œผ๋กœ ํ•œ๋‹ค. ์ฃผ์š” ์ปฌ๋Ÿผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค:

์ปฌ๋Ÿผ๋ช… ์„ค๋ช…
CODE_TY ์ฝ”๋“œ ๊ทธ๋ฃน
CODE_VALUE ์ฝ”๋“œ ๊ฐ’
CODE_NM ์ฝ”๋“œ ์ด๋ฆ„
DSPLY_ORDR ์ •๋ ฌ ์ˆœ์„œ
USE_AT ์‚ฌ์šฉ ์—ฌ๋ถ€

๐Ÿงฑ ๋ฐฑ์—”๋“œ ๊ตฌ์กฐ ์„ค๋ช…

๐Ÿ“‘ VO: CommonCode.java

@Data
public class CommonCode {
    private int codeId;
    private String codeTy;
    private String codeValue;
    private String codeNm;
    private int dsplyOrdr;
    private String useAt;
}

 

๐Ÿ“‘ DTO: GroupedCodeResponse.java

@Data
public class GroupedCodeResponse {
    private String groupCode;
    private List<CodeItem> codes;

    @Data
    public static class CodeItem {
        private String code;
        private String name;
    }
}

 

๐Ÿ“‘ Mapper: CommonCodeMapper.java

@Mapper
public interface CommonCodeMapper {
    List<CommonCode> selectCodesByTypes(@Param("types") List<String> types);
}
<select id="selectCodesByTypes" resultType="com.example.common.vo.CommonCode">
    SELECT 
        CODE_ID,
        CODE_TY AS codeTy,
        CODE_VALUE AS codeValue,
        CODE_NM AS codeNm,
        DSPLY_ORDR AS dsplyOrdr,
        USE_AT AS useAt
    FROM TB_CMMN_CODE
    WHERE CODE_TY IN
    <foreach collection="types" item="type" open="(" separator="," close=")">
        #{type}
    </foreach>
    ORDER BY CODE_TY, DSPLY_ORDR
</select>

 

๐Ÿ“‘ Service: CommonCodeService.java

public List<GroupedCodeResponse> getGroupedCodes(List<String> types) {
    List<CommonCode> codes = mapper.selectCodesByTypes(types);

    return codes.stream()
        .collect(Collectors.groupingBy(CommonCode::getCodeTy))
        .entrySet()
        .stream()
        .map(entry -> {
            GroupedCodeResponse response = new GroupedCodeResponse();
            response.setGroupCode(entry.getKey());

            List<GroupedCodeResponse.CodeItem> codeItems = entry.getValue().stream()
                .map(code -> {
                    GroupedCodeResponse.CodeItem item = new GroupedCodeResponse.CodeItem();
                    item.setCode(code.getCodeValue());
                    item.setName(code.getCodeNm());
                    return item;
                }).collect(Collectors.toList());

            response.setCodes(codeItems);
            return response;
        }).collect(Collectors.toList());
}

 

๐Ÿงฉ ์„œ๋น„์Šค๋ ˆ์ด์–ด ๋ชฉํ‘œ

  • List<CmmnCodeDTO> ๋ฅผ List<GroupedCodeResponseDTO> ๋กœ ๋ฐ”๊พธ๋Š” ๊ฒŒ ๋ชฉ์ .
  • ์ฆ‰, ๊ฐ ๊ทธ๋ฃน์ฝ”๋“œ(codeTy) ๋ณ„๋กœ codeValue, codeNm ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ฌถ์–ด ๋ฐ˜ํ™˜.

 

๐Ÿ”„ stream ์ดํ›„ ํ๋ฆ„ ํ•ด์„

return codes.stream()
  • codes๋Š” List<CmmnCodeDTO>.
  • ์ด๊ฑธ Stream<CmmnCodeDTO>๋กœ ๋ฐ”๊ฟ”์„œ ์ฒ˜๋ฆฌ ์‹œ์ž‘.

 

๐Ÿ“Œ Step 1: groupingBy

.collect(Collectors.groupingBy(CmmnCodeDTO::getCodeTy))
  • CmmnCodeDTO ๊ฐ์ฒด๋“ค์„ codeTy ๊ฐ’๋ณ„๋กœ **Map<String, List<CmmnCodeDTO>>`๋กœ ๋ฌถ์Œ.

    ์˜ˆ๋ฅผ ๋“ค๋ฉด:
{
  "position": [CmmnCodeDTO(...), CmmnCodeDTO(...)],
  "skill_level": [CmmnCodeDTO(...), ...]
}

 

๐Ÿ“Œ Step 2: entrySet().stream()

.entrySet().stream()
  • ์œ„์—์„œ ๋งŒ๋“  Map<String, List<CmmnCodeDTO>>๋ฅผ ๋‹ค์‹œ ์ŠคํŠธ๋ฆผ์œผ๋กœ ๋ณ€ํ™˜.
  • entry๋Š” Map.Entry<String, List<CmmnCodeDTO>> ํƒ€์ž….

โ˜๏ธ Map<K, V>๋Š” ์ˆœํšŒํ•˜๊ฑฐ๋‚˜ ๊บผ๋‚ผ ๋•Œ .entrySet(), keySet(), values() 3๊ฐ€์ง€ ์ค‘ ํ•˜๋‚˜ ์„ ํƒํ•œ๋‹ค.

 

 

๐Ÿ“Œ Step 3: ๊ฐ ๊ทธ๋ฃน๋ณ„๋กœ DTO ๋งŒ๋“ค๊ธฐ

.map(entry -> {
    GroupedCodeResponseDTO response = new GroupedCodeResponseDTO();
    response.setGroupCode(entry.getKey());
  • GroupedCodeResponseDTO๋Š” ํ•˜๋‚˜์˜ ๊ทธ๋ฃน์„ ํ‘œํ˜„ํ•˜๋Š” ๊ฐ์ฒด.
  • ์˜ˆ: groupCode = "position"

 

๐Ÿ“Œ Step 4: codeItems ๋งŒ๋“ค๊ธฐ

List<GroupedCodeResponseDTO.CodeItem> codeItems = entry.getValue().stream()
        .map(code -> {
            GroupedCodeResponseDTO.CodeItem item = new GroupedCodeResponseDTO.CodeItem();
            item.setCode(code.getCodeValue());
            item.setName(code.getCodeNm());
            return item;
        }).collect(Collectors.toList());
  • entry.getValue()๋Š” ๊ฐ™์€ codeTy๋ฅผ ๊ฐ€์ง„ CmmnCodeDTO ๋ฆฌ์ŠคํŠธ.
  • ๊ฐ CmmnCodeDTO๋ฅผ CodeItem(code, name)์œผ๋กœ ๋ณ€ํ™˜ํ•ด์„œ ๋ฆฌ์ŠคํŠธ๋กœ ๋งŒ๋“ฌ.

์˜ˆ:

[
  { "code": "dev", "name": "๊ฐœ๋ฐœ์ž" },
  { "code": "design", "name": "๋””์ž์ด๋„ˆ" }
]

 

๐Ÿ“Œ Step 5: ์ตœ์ข… ์กฐ๋ฆฝ

response.setCodes(codeItems);
return response;
  • GroupedCodeResponseDTO์— ์ฝ”๋“œ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ถ™์—ฌ์„œ ํ•˜๋‚˜์˜ ๊ทธ๋ฃน ์™„์„ฑ.

 

๐Ÿ“Œ Step 6: ์ „์ฒด collect

}).collect(Collectors.toList());
  • ์œ„ ๊ณผ์ •์„ ๊ฑฐ์นœ ๋ชจ๋“  ๊ทธ๋ฃน์„ ๋ฆฌ์ŠคํŠธ๋กœ ๋ชจ์•„์„œ ๋ฆฌํ„ด.

 

๐Ÿ“ฆ ์ตœ์ข… ๊ฒฐ๊ณผ ํ˜•ํƒœ ์˜ˆ์‹œ

[
  {
    "groupCode": "position",
    "codes": [
      { "code": "dev", "name": "๊ฐœ๋ฐœ์ž" },
      { "code": "design", "name": "๋””์ž์ด๋„ˆ" }
    ]
  },
  {
    "groupCode": "skill_level",
    "codes": [
      { "code": "junior", "name": "์ดˆ๊ธ‰" },
      { "code": "senior", "name": "๊ณ ๊ธ‰" }
    ]
  }
]
 

 

๐Ÿ“‘ Controller: CommonCodeController.java

@RestController
@RequestMapping("/api/common-codes")
@RequiredArgsConstructor
public class CommonCodeController {

    private final CommonCodeService service;

    @PostMapping("/batch")
    public List<GroupedCodeResponse> getGroupedCodes(@RequestBody List<String> types) {
        return service.getGroupedCodes(types);
    }
}

๐Ÿงช ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ํ˜ธ์ถœ ์˜ˆ์‹œ (Axios)

axios.post('/api/common-codes/batch', ["position", "skill_level", "team"])
  .then(res => {
    console.log(res.data);
  });

โœ… ์ •๋ฆฌ

  • ๊ณตํ†ต ์ฝ”๋“œ ๊ทธ๋ฃน์„ ํ•œ ๋ฒˆ์— ์š”์ฒญํ•ด ํ”„๋ก ํŠธ์˜ ๋กœ๋”ฉ ์‹œ๊ฐ„์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.
  • ์ฝ”๋“œ ๊ทธ๋ฃน๋ณ„๋กœ ๋ฌถ์–ด์„œ ์‘๋‹ต์„ ๋ฐ›์œผ๋ฉด ์ปดํฌ๋„ŒํŠธ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์ด ๋†’์•„์ง„๋‹ค.
  • ์ •๋ ฌ, ์‚ฌ์šฉ์—ฌ๋ถ€ ํ•„ํ„ฐ๋ง๋„ ์ถ”๊ฐ€๋กœ ๊ณ ๋ ค ๊ฐ€๋Šฅํ•˜๋‹ค.

๐Ÿ“Œ ํ™•์žฅ ์•„์ด๋””์–ด

  • Redis ๋“ฑ ์บ์‹œ ์ ์šฉ์œผ๋กœ ์†๋„ ๊ฐœ์„ 
  • ์ฝ”๋“œ ์ถ”๊ฐ€/์ˆ˜์ •/์‚ญ์ œ์— ๋Œ€ํ•œ ๊ด€๋ฆฌ ๋ฐฑ์˜คํ”ผ์Šค ๊ตฌํ˜„
  • Swagger ๋ฌธ์„œํ™” ๋ฐ ํ…Œ์ŠคํŠธ ์ž๋™ํ™”

๋Œ“๊ธ€