kccf/.claude/skills/egov-crud-module/references/patterns.md
ythong 80b5ed1da4 Initial commit - KCCF 문화원연합회 누리집
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 11:38:15 +09:00

8.4 KiB

eGovFramework KCCF 코드 패턴 레퍼런스

VO 완성 패턴

package kccf.{module}.vo;

import kccf.cmm.vo.CmmBaseVO;
import lombok.*;

@Setter @Getter @ToString
@NoArgsConstructor @AllArgsConstructor
public class {Module}VO extends CmmBaseVO {

    /* {한글명} 아이디 */
    private String {module}Id;
    /* {한글명}명 */
    private String {module}Nm;
    /* 지역코드 */
    private String areaCode;
    /* 삭제여부 */
    private String deleteAt;
    /* 등록자 아이디 */
    private String regUserId;
    /* 등록자명 */
    private String regUserNm;
    /* 등록일시 */
    private String regDttm;
    /* 수정자 아이디 */
    private String updUserId;
    /* 수정일시 */
    private String updDttm;

    /* 검색 - {조건명} */
    private String search{Field};

    /* 권한 관련 */
    private boolean canReadOther;
    private boolean canWrite;
    private boolean canUpdate;
}

DAO 패턴

package kccf.{module}.dao;

import java.util.List;
import kccf.cmm.dao.CmmBaseDAO;
import kccf.{module}.vo.{Module}VO;
import org.springframework.stereotype.Repository;

@Repository
public class {Module}DAO extends CmmBaseDAO {

    public List<{Module}VO> select{Module}List({Module}VO vo) {
        return selectList("kccf.{module}.dao.{Module}DAO.select{Module}List", vo);
    }

    public {Module}VO select{Module}Detail({Module}VO vo) {
        return selectOne("kccf.{module}.dao.{Module}DAO.select{Module}Detail", vo);
    }

    public void insert{Module}({Module}VO vo) {
        insert("kccf.{module}.dao.{Module}DAO.insert{Module}", vo);
    }

    public void update{Module}({Module}VO vo) {
        update("kccf.{module}.dao.{Module}DAO.update{Module}", vo);
    }

    public void delete{Module}({Module}VO vo) {
        delete("kccf.{module}.dao.{Module}DAO.delete{Module}", vo);
    }

    public String get{Module}IdSeq() {
        return selectOne("kccf.{module}.dao.{Module}DAO.get{Module}IdSeq", null);
    }
}

Service / ServiceImpl 패턴

// Service 인터페이스
public interface {Module}Service {
    List<{Module}VO> select{Module}List({Module}VO vo);
    {Module}VO select{Module}Detail({Module}VO vo);
    void insert{Module}({Module}VO vo);
    void update{Module}({Module}VO vo);
    void delete{Module}({Module}VO vo);
    String get{Module}IdSeq();
}

// ServiceImpl
@Service
@RequiredArgsConstructor
public class {Module}ServiceImpl implements {Module}Service {
    private final {Module}DAO {module}DAO;

    @Override public List<{Module}VO> select{Module}List({Module}VO vo) { return {module}DAO.select{Module}List(vo); }
    // ... 나머지 메서드
}

Controller 완성 패턴

@Controller
@RequiredArgsConstructor
@RequestMapping(value = "/sec/{module}")
public class {Module}Controller extends CmmBaseController {

    private final {Module}Service {module}Service;
    private final KccfLoginService kccfLoginService;

    @RequestMapping(value = "/{module}ListPage.do")
    public String {module}ListPage({Module}VO searchVO, ModelMap model, HttpSession session) {
        UserVO userVO = (UserVO)session.getAttribute("user");
        if(userVO == null || StringUtils.isAllBlank(userVO.getUserId())) {
            return "redirect:/login.do";
        }
        // 코드 목록 로딩
        CmmnCodeDetailVO cmmVO = new CmmnCodeDetailVO();
        cmmVO.setCode("AREA_CODE");
        model.addAttribute("areaCodeList", kccfLoginService.selectCodeListAjax(cmmVO));
        searchVO.setCanWrite(AuthUtil.canWrite(getUserId(), getSiteSeCode(), getSelMenuId(), ""));
        model.addAttribute("searchVO", searchVO);
        return model.getAttribute("hostId") + "/{module}/list{Module}";
    }

    @RequestMapping(value = "/select{Module}ListAjax.do")
    public String select{Module}ListAjax({Module}VO searchVO, ModelMap model) {
        List<{Module}VO> list = {module}Service.select{Module}List(searchVO);
        if(list != null && list.size() > 0) {
            searchVO.setTotCnt(list.get(0).getTotCnt());
        }
        model.addAttribute("data", list);
        model.addAttribute("searchVO", searchVO);
        return "jsonView";
    }

    @RequestMapping(value = "/{module}DetailPage.do")
    public String {module}DetailPage({Module}VO searchVO, ModelMap model, HttpSession session) {
        UserVO userVO = (UserVO)session.getAttribute("user");
        if(userVO == null || StringUtils.isAllBlank(userVO.getUserId())) {
            return "redirect:/login.do";
        }
        if(searchVO.get{Module}Id() != null && !searchVO.get{Module}Id().isEmpty()) {
            model.addAttribute("{module}VO", {module}Service.select{Module}Detail(searchVO));
        }
        searchVO.setCanWrite(AuthUtil.canWrite(getUserId(), getSiteSeCode(), getSelMenuId(), ""));
        model.addAttribute("searchVO", searchVO);
        return model.getAttribute("hostId") + "/{module}/{module}Regist";
    }

    @RequestMapping(value = "/insert{Module}.do")
    public String insert{Module}({Module}VO searchVO, RedirectAttributes redirect) {
        searchVO.setRegUserId(getUserId());
        searchVO.setUpdUserId(getUserId());
        if(searchVO.get{Module}Id() != null && !searchVO.get{Module}Id().isEmpty()) {
            {module}Service.update{Module}(searchVO);
        } else {
            searchVO.set{Module}Id({module}Service.get{Module}IdSeq());
            {module}Service.insert{Module}(searchVO);
        }
        redirect.addAttribute("pageNo", searchVO.getPageNo());
        redirect.addAttribute("selMenuId", searchVO.getSelMenuId());
        return "redirect:/sec/{module}/{module}ListPage.do";
    }

    @RequestMapping(value = "/delete{Module}.do")
    public String delete{Module}({Module}VO searchVO, RedirectAttributes redirect) {
        {module}Service.delete{Module}(searchVO);
        redirect.addAttribute("pageNo", searchVO.getPageNo());
        redirect.addAttribute("selMenuId", searchVO.getSelMenuId());
        return "redirect:/sec/{module}/{module}ListPage.do";
    }
}

MyBatis Mapper XML 패턴

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="kccf.{module}.dao.{Module}DAO">

    <select id="select{Module}List" parameterType="{Module}VO" resultType="{Module}VO">
        SELECT A.*
             , ROW_NUMBER() OVER (ORDER BY A.REG_DTTM DESC) AS rn
             , COUNT(*) OVER () AS totCnt
          FROM TB_{MODULE} A
         WHERE A.DELETE_AT = 'N'
        <where>
            <if test="searchAreaCode != null and searchAreaCode != ''">
                AND A.AREA_CODE = #{searchAreaCode}
            </if>
            <if test="search{Module}Nm != null and search{Module}Nm != ''">
                AND A.{MODULE}_NM LIKE '%' || #{search{Module}Nm} || '%'
            </if>
        </where>
        ORDER BY A.REG_DTTM DESC
    </select>

    <select id="select{Module}Detail" parameterType="{Module}VO" resultType="{Module}VO">
        SELECT A.*
          FROM TB_{MODULE} A
         WHERE A.{MODULE}_ID = #{module}Id}
    </select>

    <insert id="insert{Module}" parameterType="{Module}VO">
        INSERT INTO TB_{MODULE} (
            {MODULE}_ID, {MODULE}_NM, AREA_CODE,
            DELETE_AT, REG_USER_ID, REG_DTTM, UPD_USER_ID, UPD_DTTM
        ) VALUES (
            #{module}Id}, #{module}Nm}, #{areaCode},
            'N', #{regUserId}, SYSDATE, #{updUserId}, SYSDATE
        )
    </insert>

    <update id="update{Module}" parameterType="{Module}VO">
        UPDATE TB_{MODULE}
           SET {MODULE}_NM = #{module}Nm}
             , AREA_CODE   = #{areaCode}
             , UPD_USER_ID = #{updUserId}
             , UPD_DTTM    = SYSDATE
         WHERE {MODULE}_ID = #{module}Id}
    </update>

    <update id="delete{Module}" parameterType="{Module}VO">
        UPDATE TB_{MODULE}
           SET DELETE_AT   = 'Y'
             , UPD_USER_ID = #{updUserId}
             , UPD_DTTM    = SYSDATE
         WHERE {MODULE}_ID = #{module}Id}
    </update>

    <select id="get{Module}IdSeq" resultType="String">
        SELECT LPAD(NVL(MAX(TO_NUMBER({MODULE}_ID)), 0) + 1, 10, '0')
          FROM TB_{MODULE}
    </select>

</mapper>

주의사항

  • SYSDATE: Oracle/Altibase 전용 → MySQL/MariaDB에서는 NOW()
  • ROW_NUMBER() OVER (...): Oracle/Altibase/PostgreSQL 지원 → MySQL 5.7 이하 미지원 (서브쿼리 대체)
  • || 문자열 연결: Oracle/PostgreSQL → MySQL은 CONCAT()
  • 현재 DB 설정은 egov-com-datasource.xml 또는 application 설정에서 확인