🏛️ 홍익인간CMS 개발자 매뉴얼

Version 202506 | Updated 2025-06-12
🌐 언어 선택: 🇰🇷 한국어 🇲🇳 Монгол 🇺🇸 English

5. 📁 코드 구조 및 패턴

5.1 MVC 패턴 구현

홍익인간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 아키텍처 구조

홍익인간CMS의 MVC 아키텍처는 전자정부 표준프레임워크의 아키텍처를 준수하며, 다음과 같은 특징을 가집니다:

🎮Controller 계층

  • 전자정부 표준 아키텍처를 준수
  • HumanAbstractController를 상속받아 구현
  • Controller 클래스에서 직접 DAO 클래스의 메소드를 호출할 수 없음
  • Service 계층을 통해 비즈니스 로직 처리

👁️View 계층

  • JSP, Tiles를 활용한 화면 구성
  • JSTL, EL을 활용한 데이터 표현
  • 사용자 정의 태그 라이브러리 활용

📊Model 계층

  • VO(Value Object) 클래스를 통한 데이터 전달
  • Service 계층에서 비즈니스 로직 처리 후 결과 반환

5.3 서비스 아키텍처 구조

홍익인간CMS의 서비스 아키텍처는 다음과 같은 특징을 가집니다:

🔌서비스 인터페이스

  • 모든 서비스는 인터페이스로 정의
  • "service" 패키지에 인터페이스 위치

⚙️서비스 구현체

  • EgovAbstractServiceImpl을 확장하여 구현
  • "service/impl" 패키지에 구현체 위치
  • @Service 어노테이션을 통한 빈 등록

🔄트랜잭션 관리

  • @Transactional 어노테이션을 통한 트랜잭션 관리
  • 선언적 트랜잭션 관리 방식 적용

5.4 데이터 액세스 아키텍처 구조

홍익인간CMS의 데이터 액세스 아키텍처는 다음과 같은 특징을 가집니다:

🗄️DAO 클래스

  • 전자정부 표준 아키텍처를 준수
  • EgovAbstractMapper를 확장한 HumanAbstractMapper를 상속받아 구현
  • @Repository 어노테이션을 통한 빈 등록

🗺️MyBatis 매퍼

  • XML 기반 SQL 관리
  • 동적 쿼리 지원
  • 다양한 데이터베이스 지원 (CUBRID, Oracle, MySQL, MS-SQL, Tibero)

🔗데이터베이스 연결

  • JNDI 기반 데이터소스 설정
  • 프로필별 데이터소스 설정 (local, dev, prd)

5.5 AOP 활용

홍익인간CMS는 관점 지향 프로그래밍(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 인터셉터 및 필터

홍익인간CMS는 인터셉터와 필터를 활용하여 요청 처리 전후에 공통 작업을 수행합니다. 이 섹션에서는 인터셉터와 필터의 구조적 측면을 설명합니다. 보안 관련 상세 내용은 07 보안가이드 문서를 참조하세요.

5.6.1 필터 구조

홍익인간CMS는 보안 및 데이터 전처리 강화를 위해 다양한 필터를 web.xml에 등록하여 사용합니다.
필터는 서블릿 컨테이너 레벨에서, web.xml에 정의된 순서대로 모든 HTTP 요청에 대해 체인 방식으로 동작합니다.

주요 필터 및 적용 경로

필터명 적용 URL 역할/설명
CharacterEncodingFilter /* 모든 요청/응답의 문자 인코딩을 UTF-8로 통일
HTMLTagFilter /* 입력 파라미터에서 위험한 HTML 태그 제거
CrossUrlScriptingFilter (XSS) /* 크로스사이트 스크립팅(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 인터셉터 구조

홍익인간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> 설정에 따릅니다.