🏛️ HONGIKINGAN CMS хөгжүүлэгчийн гарын авлага

Хувилбар 202506 | Шинэчилсэн 2025-06-12
🌐 Хэл сонгох: 🇰🇷 한국어 🇲🇳 Монгол 🇺🇸 English

05. 📁 Код бүтэц болон загвар

5.1 MVC загвар хэрэгжүүлэлт

HONGIKINGAN CMS нь Spring MVC загварт суурилан хэрэгжүүлэгдсэн бөгөөд давхарга бүр тодорхой тусгаарлагдсан байдаг.

5.1.1 Controller бүтэц

Контроллер нь клиентийн хүсэлтийг хүлээн авч, тохирох сервисийг дуудаж, үр дүнг харагдах хэсэгт дамжуулах үүрэгтэй.

Үндсэн онцлог

Контроллер жишээ (бодит код)

[Кодын байршил] humanframe.backoffice.controller.bbs.AdminBbsController

@Controller
@RequestMapping("/${humanframe.admin.path}/bbs")
public class AdminBbsController extends HumanAbstractController {

    @Resource(name="bbsSettingService")
    private BbsSettingService bbsSettingService;

    @Resource(name="siteService")
    private SiteService siteService;

    @Autowired
    private MngrSession mngrSession;

    @SuppressWarnings("unchecked")
    @RequestMapping("list")
    public String list(HttpServletRequest request, Model model) throws Exception {
        HumanListVO listVO = new HumanListVO(request);
        if(mngrSession.getAuthorTy().equals("2") || mngrSession.getAuthorTy().equals("3")){//админ
            listVO.setParam("siteNos", mngrSession.getSiteNos().length > 0?mngrSession.getSiteNos():new String[]{"0"});
            listVO.setParam("crtrId",mngrSession.getMngrId());
            if(mngrSession.getAuthorTy().equals("2")) {
                listVO.setParam("bbsNos", "");
            }else {
                if(mngrSession.getSiteMenus() != null) {
                    listVO.setParam("bbsNos", mngrSession.getBbsNos().length > 0?mngrSession.getBbsNos():new String[]{"0"});
                }else {
                    listVO.setParam("bbsNos", new String[] {"0"});
                }
            }
        }
        listVO = bbsSettingService.boardListVo(listVO);

        List<BbsCtgryVO> bbsCtgryList = bbsSettingService.listCtgry(0, null, "BBS");

        Map<String, String> paramMap = new HashMap<String, String>();
        paramMap.put("useAt", "Y");
        List<SiteVO> siteList = siteService.selectSiteListAll(paramMap);

        model.addAttribute("listVO", listVO);
        model.addAttribute("useAt", CodeMap.USE_AT);
        model.addAttribute("bbsTyCode", CommCodeMap.BBS_TY);
        model.addAttribute("siteList", siteList);
        model.addAttribute("bbsCtgryList", bbsCtgryList);

        return "/admin/bbs/list";
    }
    
    // бусад методууд...
}

5.1.2 Service давхарга

Сервис давхарга нь бизнес логикийг боловсруулж, гүйлгээг удирдах үүрэгтэй.

Үндсэн онцлог

Сервис интерфейс жишээ (бодит код)

[Кодын байршил] humanframe.backoffice.service.BbsSettingService

@SuppressWarnings("rawtypes")
public interface BbsSettingService {
    
    /**
     * Самбарын жагсаалтыг харна.
     * 
     * @param listVO Хайлтын нөхцөл
     * @return Самбарын жагсаалт
     */
    public HumanListVO boardListVo(HumanListVO listVO) throws Exception;
    
    /**
     * Самбарын мэдээллийг харна.
     * 
     * @param bbsNo Самбарын дугаар
     * @return Самбарын мэдээлэл
     */
    public BbsSettingVO retrieveBoardSetting(int bbsNo) throws Exception;
    
    /**
     * Самбарын ангиллын жагсаалтыг харна.
     * 
     * @param bbsNo Самбарын дугаар
     * @param prefix Угтвар үг
     * @param type Төрөл
     * @return Ангиллын жагсаалт
     */
    public List<BbsCtgryVO> listCtgry(int bbsNo, String prefix, String useTy) throws Exception;
    
    // бусад методууд...
}
Сервис хэрэгжүүлэлтийн жишээ (бодит код)

[Кодын байршил] humanframe.backoffice.service.impl.BbsSettingServiceImpl

@SuppressWarnings("rawtypes")
@Service("bbsSettingService")
public class BbsSettingServiceImpl extends EgovAbstractServiceImpl implements BbsSettingService {

    @Resource(name="bbsSettingDAO")
    private BbsSettingDAO bbsSettingDAO;
    
    @Resource(name="bbsScrtyDAO")
    private BbsScrtyDAO bbsScrtyDAO;
    
    @Override
    public HumanListVO boardListVo(HumanListVO listVO) throws Exception {
        return bbsSettingDAO.selectBoardListVO(listVO);
    }
    
    @Override
    public BbsSettingVO retrieveBoardSetting(int bbsNo) throws Exception {
        BbsSettingVO bbsSettingVO = bbsSettingDAO.selectBoardSetting(bbsNo, 0);

        if(bbsSettingVO.getCtgryUseAt().equals("Y")) {
            List<BbsCtgryVO> ctgryList = bbsSettingDAO.listCtgry(bbsNo, "L", "NTT");
            bbsSettingVO.setCtgryList(ctgryList);
        }
        
        // ... дунд хэсэг орхигдсон (аюулгүй байдлын тохиргооны логик) ...
        
        return bbsSettingVO;
    }
    
    @Override
    public List<BbsCtgryVO> listCtgry(int bbsNo, String prefix, String useTy) throws Exception {
        return bbsSettingDAO.listCtgry(bbsNo, prefix, useTy);
    }
    
    // бусад методууд...
}

5.1.3 DAO болон MyBatis ашиглалт

DAO (Data Access Object) нь өгөгдлийн сантай харилцах үүрэгтэй бөгөөд MyBatis ашиглан SQL асуулгыг удирддаг.

Үндсэн онцлог

DAO жишээ (бодит код)

[Кодын байршил] humanframe.backoffice.dao.BbsSettingDAO

@Repository("bbsSettingDAO")
public class BbsSettingDAO extends HumanAbstractMapper {

    public HumanListVO selectBoardListVO(HumanListVO listVO) throws Exception {
        return selectListVO(setDomain("boardSetting.selectBoardCount"), setDomain("boardSetting.selectBoardListVO"), listVO);
    }
    
    public BbsSettingVO selectBoardSetting(int bbsNo, int siteNo) throws Exception {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("bbsNo", bbsNo);
        if(siteNo > 0){
            paramMap.put("siteNo", siteNo);
        }
        BbsSettingVO bbsSettingVO = (BbsSettingVO)selectOne(setDomain("boardSetting.selectBoardSetting"), paramMap);
        return bbsSettingVO;
    }
    
    public List<BbsCtgryVO> listCtgry(int bbsNo, String prefix, String useTy) throws Exception {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("bbsNo", bbsNo);
        paramMap.put("prefix", prefix);
        paramMap.put("ctgryUseTy", useTy);
        return selectList(setDomain("boardCtgry.listCtgry"), paramMap);
    }
    
    // бусад методууд...
}
MyBatis маппер XML жишээ (бодит код)

[Кодын байршил] humanframe/sqlmap/mappers/mysql/human-bbs-ctgry.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="humanframe.boardCtgry">

    <resultMap type="bbsCtgryVO" id="bbsCtgryMap">
        <result property="bbsNo"       column="bbs_no" />
        <result property="ctgryNo"     column="ctgry_no" />
        <result property="ctgryValue"  column="ctgry_value" />
        <result property="ctgryNm"     column="ctgry_nm" />
    </resultMap>

    <select id="listCtgry" parameterType="hashmap" resultMap="bbsCtgryMap">
    SELECT
          bbs_no
        , ctgry_no
        <choose>
            <when test="prefix != null">
            , CONCAT('${prefix}-' , ctgry_no) AS ctgry_value
            </when>
            <otherwise>
            , '' AS ctgry_value
            </otherwise>
        </choose>
        , ctgry_nm
    FROM tn_bbs_ctgry
    WHERE bbs_no = #{bbsNo}
        AND use_at = 'Y'
    </select>

    <!-- бусад асуулга... -->
</mapper>

5.2 MVC архитектурын бүтэц

HONGIKINGAN CMS-ийн MVC архитектур нь eGovFrame стандарт фреймворкийн архитектурыг дагаж, дараах онцлогтой:

🎮Controller давхарга

  • eGovFrame стандарт архитектур дагах
  • HumanAbstractController-ээс удамшуулан хэрэгжүүлэх
  • Controller класс нь DAO классын методыг шууд дуудаж болохгүй
  • Service давхаргаар дамжуулан бизнес логик боловсруулах

👁️View давхарга

  • JSP, Tiles ашиглан дэлгэц бүрдүүлэх
  • JSTL, EL ашиглан өгөгдөл илэрхийлэх
  • Хэрэглэгчийн тодорхойлсон таг номын сан ашиглах

📊Model давхарга

  • VO (Value Object) классаар дамжуулан өгөгдөл дамжуулах
  • Service давхаргаас бизнес логик боловсруулсны дараа үр дүн буцаах

5.3 Сервисийн архитектурын бүтэц

HONGIKINGAN CMS-ийн сервисийн архитектур нь дараах онцлогтой:

🔌Сервисийн интерфейс

  • Бүх сервис интерфейсээр тодорхойлогдоно
  • "service" багцад интерфейс байршуулна

⚙️Сервисийн хэрэгжүүлэлт

  • EgovAbstractServiceImpl-ийг өргөтгөж хэрэгжүүлэх
  • "service/impl" багцад хэрэгжүүлэлт байршуулна
  • @Service аннотацаар дамжуулан bean бүртгэх

🔄Гүйлгээний удирдлага

  • @Transactional аннотацаар дамжуулан гүйлгээ удирдах
  • Зарлалт гүйлгээ удирдлагын арга хэрэглэх

5.4 Өгөгдөлд хандах архитектурын бүтэц

HONGIKINGAN CMS-ийн өгөгдөлд хандах архитектур нь дараах онцлогтой:

🗄️DAO класс

  • eGovFrame стандарт архитектур дагах
  • EgovAbstractMapper-ийг өргөтгөсөн HumanAbstractMapper-ээс удамшуулан хэрэгжүүлэх
  • @Repository аннотацаар дамжуулан bean бүртгэх

🗺️MyBatis маппер

  • XML суурьтай SQL удирдлага
  • Динамик асуулга дэмжих
  • Төрөл бүрийн өгөгдлийн сан дэмжих (CUBRID, Oracle, MySQL, MS-SQL, Tibero)

🔗Өгөгдлийн сан холболт

  • JNDI суурьтай өгөгдлийн эх тохиргоо
  • Профайл тус бүрийн өгөгдлийн эх тохиргоо (local, dev, prd)

5.5 AOP ашиглалт

HONGIKINGAN CMS нь Aspect Oriented Programming (AOP) ашиглан логлол, эрх шалгах зэрэг нийтлэг сонирхлыг модулчилж байна.

5.5.1 Логлол

Системийн лог болон хэрэглэгчийн үйл ажиллагааны логийг бүртгэхэд AOP ашигладаг.

Үндсэн функц

Логлолын AOP жишээ (бодит код)

[Кодын байршил] humanframe.backoffice.aop.aspect.admin.AdminLogAspect

@Aspect
@Order(2)
@Component
public class AdminLogAspect {

    @Resource(name = "mngLogService")
    private MngLogService mngLogService;

    // Админ эрх шалгах
    @Pointcut("execution(* humanframe.backoffice..Admin*Controller.form(..))"
            + "|| execution(* humanframe.backoffice..Admin*Controller.list(..))"
            + "|| execution(* humanframe.backoffice..Admin*Controller.view(..))"
            + "|| execution(* humanframe.backoffice..Admin*Controller.action(..))"
            + "|| execution(* humanframe.backoffice..Admin*Controller.logout(..))")
    private void checkAdminLog() {}

    @Before(value = "checkAdminLog()")
    private void beforecheckAdminLog(
            JoinPoint joinPoint)throws Exception {
        // ... дунд хэсэг орхигдсон (админ лог хадгалах логик) ...
    }
}

5.5.2 Эрх шалгах

Цэс болон функцэд хандах эрхийг шалгахад AOP ашигладаг.

Үндсэн функц

Эрх шалгах AOP жишээ (бодит код)

[Кодын байршил] humanframe.backoffice.aop.aspect.admin.AdminBbsAspect

@Aspect
@Order(1)
@Component
public class AdminBbsAspect {

    @Autowired
    private BbsSettingService bbsSettingService;

    @Autowired
    private MngrSession mngrSession;

    // Админ эрх шалгах
    @Pointcut("execution(* humanframe.backoffice..AdminBbsType*Controller.form(..))"
            + "|| execution(* humanframe.backoffice..AdminBbsType*Controller.list(..))"
            + "|| execution(* humanframe.backoffice..AdminBbsType*Controller.view(..))"
            + "|| execution(* humanframe.backoffice..AdminBbsType*Controller.action(..))"
            + "|| execution(* humanframe.backoffice..AdminBbsType*Controller.answer(..))")
    private void checkAdminBbsAuth() {}

    @Before(value = "checkAdminBbsAuth()")
    private void beforeCheckAdminBbsAuth(
            JoinPoint joinPoint)throws Exception {
        // ... дунд хэсэг орхигдсон (эрх шалгах логик) ...
    }
}

5.6 Интерсептор болон шүүлтүүр

HONGIKINGAN CMS нь интерсептор болон шүүлтүүр ашиглан хүсэлт боловсруулахын өмнө болон дараа нийтлэг ажлыг гүйцэтгэдэг. Энэ хэсэгт интерсептор болон шүүлтүүрийн бүтцийн талыг тайлбарлана. Аюулгүй байдлын дэлгэрэнгүй мэдээллийг 07 Аюулгүй байдлын удирдамж баримт бичгээс үзнэ үү.

5.6.1 Шүүлтүүрийн бүтэц

HONGIKINGAN CMS нь аюулгүй байдал болон өгөгдлийн урьдчилсан боловсруулалтыг сайжруулахын тулд төрөл бүрийн шүүлтүүрийг web.xml-д бүртгэж ашигладаг.
Шүүлтүүр нь сервлет контейнерийн түвшинд, web.xml-д тодорхойлсон дарааллаар бүх HTTP хүсэлт-д гинжин хэлбэрээр ажилладаг.

Үндсэн шүүлтүүр болон хэрэглэх зам

Шүүлтүүрийн нэр Хэрэглэх URL Үүрэг/Тайлбар
CharacterEncodingFilter /* Бүх хүсэлт/хариултын тэмдэгт кодчилолыг UTF-8-д нэгтгэх
HTMLTagFilter /* Оролтын параметрээс аюултай HTML тагийг арилгах
CrossUrlScriptingFilter (XSS) /* Cross-site scripting (XSS) довтолгооноос сэргийлэх
XSSWebFilter /* Нэмэлт XSS аюулгүй байдлын шүүлтүүр (бодлогоор HTML болон JS аюултай элементийг шүүх)
MberLoginFilter /* Spring Security (DelegatingFilterProxy) суурьтай нэвтрэх боловсруулалт
HumanFilter /humanmst/* Админ хуудас тусгайлан: хандалтын хяналт, баталгаажуулалт/эрх шалгах

Шүүлтүүрийн гинжний бүтэц

Шүүлтүүрийн гүйцэтгэх дараалал
Клиентийн хүсэлт
↓
[CharacterEncodingFilter]
↓
[HTMLTagFilter]
↓
[CrossUrlScriptingFilter]
↓
[XSSWebFilter]
↓
[MberLoginFilter]
↓
[HumanFilter] (※ админ хуудас (/humanmst/*) хүсэлт үед л)
↓
Сервлет (DispatcherServlet гэх мэт)
⚠️

Үйл ажиллагааны анхаарах зүйл

  • Ихэнх шүүлтүүр бүх замд (/*) хэрэглэгддэг бөгөөд HumanFilter нь админ хуудас (/humanmst/*) замд л хэрэглэгдэнэ.
  • Шүүлтүүрийн гүйцэтгэх дараалал нь web.xml-д бүртгэсэн дараалалтай ижил байдлаар ажилладаг.
  • HTML/XSS шүүлтүүрийг давхарлан хэрэглэж оролтын утгын аюулгүй байдлыг дээд зэргээр хангана.
  • Нэвтрэх/баталгаажуулалтын шүүлтүүрийг аюулгүй байдлын хувьд хойд дарааллд байрлуулна.

5.6.2 Интерсепторын бүтэц

HONGIKINGAN CMS нь HTTP хүсэлт боловсруулахын өмнө/дараа нийтлэг логикийг хэрэглэхийн тулд Spring MVC интерсептор (Interceptor) ашигладаг.
Үндсэн интерсептор нь dispatcher-servlet.xml-д бүртгэгдэж, доорх байдлаар өөр өөр зам болон үүрэгтэй.

Үндсэн интерсептор болон хэрэглэх зам

Интерсепторын нэр Хэрэглэх зам Үүрэг/Тайлбар
CMSUriInterceptor Бүх нийт (/), админ болон зарим үл хамаарах Сайтын URI хандалтын хяналт, шаардлагагүй хүсэлт хориглох гэх мэт
CMSAdminInterceptor Админ тусгайлан (/humanmst/ гэх мэт) Админ хуудас хандалт тусгайлан, баталгаажуулалт болон эрх шалгах
DeviceResolverHandlerInterceptor Бүх нийт (Spring Mobile) Төхөөрөмжийн төрөл (гар утас/PC) ялгах зориулалт

Интерсепторын гинжний бүтэц

Интерсепторын гүйцэтгэх дараалал
Клиентийн хүсэлт
↓
[DeviceResolverHandlerInterceptor] (гар утас/PC ялгах)
↓
[CMSUriInterceptor] (сайтын URI хандалт удирдах, админ үл хамаарах)
↓
[CMSAdminInterceptor] (※ админ (/humanmst/ гэх мэт) хүсэлт үед л)
↓
Контроллер (Controller)
ℹ️

Үйл ажиллагааны анхаарах зүйл

Интерсепторын хэрэглэх/үл хамаарах зам нь dispatcher-servlet.xml-ийн <mvc:interceptor> тохиргоонд тулгуурладаг.