5. 📁 코드 구조 및 패턴
5.1 MVC 패턴 구현
홍익인간CMS는 Spring MVC 패턴을 기반으로 구현되어 있으며, 각 계층이 명확히 분리되어 있습니다.
5.1.1 Controller 구조
컨트롤러는 클라이언트의 요청을 받아 적절한 서비스를 호출하고, 결과를 뷰에 전달하는 역할을 합니다.
주요 특징
- 요청 파라미터 검증
- 예외 처리
- 응답 데이터 포맷팅
- HumanAbstractController를 상속하여 구현
- 전자정부 표준 아키텍처 준수
[코드 위치] 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 계층
서비스 계층은 비즈니스 로직을 처리하고, 트랜잭션을 관리하는 역할을 합니다.
주요 특징
- 인터페이스 기반 설계
- 트랜잭션 관리
- 비즈니스 로직 구현
- DAO 호출 및 결과 처리
- EgovAbstractServiceImpl을 상속하여 구현
- 전자정부 표준 아키텍처 준수
[코드 위치] 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 쿼리를 관리합니다.
주요 특징
- 인터페이스 기반 매퍼
- XML 기반 SQL 관리
- 동적 쿼리 지원
- 결과 매핑 자동화
- EgovAbstractMapper를 상속한 HumanAbstractMapper를 상속받아 구현
- 전자정부 표준 아키텍처 준수
[코드 위치] 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);
}
// 기타 메소드...
}
[코드 위치] 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를 활용합니다.
주요 기능
- 컨트롤러 메소드 실행 로깅
- 서비스 메소드 실행 시간 측정
- 예외 발생 로깅
- 사용자 활동 로깅
[코드 위치] 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를 활용합니다.
주요 기능
- 메뉴 접근 권한 검증
- 게시판 권한 검증
- 기능 권한 검증
[코드 위치] 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> 설정에 따릅니다.