💬
목차
< 뒤로가기
인쇄

오픈소스와 비교하여 캐모마일을 사용해야 하는 이유

제품소개서 내 오픈소스 프레임워크 비교

오픈소스의 경우 개발자 역량에 의존적이며 프로젝트 진행 외 향후 유지보수 관점에서도 문제 발생 시 대처가 어렵다는 단점이 있습니다. 캐모마일의 경우 Spring의 장점을 포함하여 그룹사 환경에 적합한, 기술지원이 가능한 전담인력, 지속가능한 프레임워크입니다.

image-20220331172324348

개발 라이선스와 운영 라이선스

캐모마일의 라이선스는 아래와 같은 종류를 가집니다.

중분류설명캐모마일
정식 라이선스정식 라이선스 체결 후 배포되는 라이선스.
운영 환경에서 사용하며, 서버의 코어수 검증기간을 검증합니다.
✔ 운영 서버에 적용
개발 라이선스개발 시 사용하기 위한 라이선스이며, 특정 기간동안 발급 된다.✔ 2개월 (로컬 및 개발 서버에 적용)
체험 라이선스초기 체험 을 위한 라이선스이며, 짧은 기간 동안 사용할 수 있는 라이선스이다.✔ 2주 (기간이 만료되는 경우 동작하지 않음)

최종 사용자 라이선스 동의(EULA) 문서는 캐모마일 바이너리 배포 파일내 존재합니다.

image-20220404145633628

캐모마일을 사용하는 경우 필요한 인스턴스의 수는?

기본 하나의 인스턴스(어드민)가 필요하며, 모바일용 모바일 AppStore 서버 및 배치 서버가 필요합니다.

Redis를 데이터 저장소로 사용하는 경우 별도 인스턴스 필요합니다.

어드민을 반드시 사용해야 하는지?

어플리케이션의 동작과는 상관없으며, 어플리케이션을 관리하는 기능들이 많기에 사용하기를 권장합니다.

레디스를 반드시 사용해야 하는지?

캐모마일을 사용할 떄 Redis는 사용되어져야 합니다.

내부적으로는 프로젝트에서 개발하는 어플리케이션과 캐모마일 어드민의 통신 용도(pub/sub)로 사용됩니다. 부가적으로 캐시 용도로 사용하고 있습니다.

그렇기 때문에 기본적으로는 내장형 Redis를 제공하고 있습니다. (별도로 설치하지 않더라도 동작 가능하도록 구성되어져 있음)

데이터 저장소 혹은 세션 저장소로 Redis를 추가활용하시는 경우 설치형으로 구성하기를 권장 드립니다.

설치형으로 사용하실 때에는 3중화를 권장

Redis로 세션클러스터링 구성 시 고려해야 하는 사항은 어떠한 것들이 있나요? Redis로 세션클러스터링을 구성하기 위해서는 프로젝트 팀에서 충분한 검토가 선행되어져야 합니다. 프로덕션 환경에서 Redis를 세션클러스터링으로 사용할 때 고려사항은 아래와 같습니다.

  • 최소 Master/Slave 구조를 가져갈 것 (3중화 구성을 권장합니다.)
  • 클러스터를 사용하는 경우 Pool을 설정
  • Redis 엔터프라이즈 버전을 통한 모니터링
  • 세션 적재 대상 및 적재 사이즈 검토
    • 세션에 적재되는 대상은 java.io.Serializable을 구현하여야 합니다.
  • 세션 만료 처리로 인한 부하 검토
    • 기본적으로 분단위 세션 만료 처리 위 항목 외에도 반드시 프로덕션 환경에서의 성능테스트를 수행하여 잠재적인 문제들을 확인해야 합니다.

캐모마일 가이드 문서 및 Java doc

캐모마일의 가이드 문서는 바이너리 파일 배포본 안에 있습니다.

가이드 문서 : chamomile-distribution-{버전}-bin/chamomile-distribution-{버전}/docs/doc

image-20220620154431188

가이드 문서는 현재 10종으로 제공하고 있습니다.

Java doc : chamomile-distribution-{버전}-bin/chamomile-distribution-{버전}/docs/javadoc

image-20220404164824366

버전 업데이트는 어떻게 할 수 있는지?

프로젝트에서 이슈 발생으로 긴급하게 패치가 되는 경우 가이드에 따라 업데이트를 수행하게 되며, 일반적으로 아래와 같이 pom.xml의 캐모마일 버전을 업데이트 합니다.

<properties>
    <chamomile.version>2.2.1-SNAPSHOT</chamomile.version>
</properties>

<!-- 일부 모듈만 업데이트를 수행하는 경우 아래와 같이 해당 버전만을 업데이트 수행한다. -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>net.lotte.chamomile</groupId>
            <artifactId>chamomile-mobile</artifactId>
            <version>2.2.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
</dependencyManagement>

참고로 라이브러리의 버전 관리는 dependencyManagement를 이용하여 관리 된다.

이미 적용 된 프로젝트에 신규 버전으로 업데이트를 수행하는 경우 배포되는 캐모마일 바이너리 파일의 chamomile-distribution-{버전}-RELEASE/docs/doc 폴더 내 버전업데이트_가이드-{버전}.pdf 문서를 참고하여 업데이트를 수행한다.

빌드 시 캐모마일의 라이브러리가 다운로드 되지 않아요

캐모마일에서 배포하는 개발환경에는 라이브러리를 다운로드 받기 위한 정보가 설정되어 있습니다.

  • maven의 settings.xml 파일
  • 프로젝트의 pom.xml 파일

maven의 설정 파일인 C:/Chamomile/maven/settings.xml에 mirror 정보가 설정되어져 있으며,

<?xml version="1.0" encoding="UTF-8"?>

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

    <!-- ... -->
    <mirrors>
        <mirror>
            <id>chamomile</id>
            <mirrorOf>chamomile</mirrorOf>
            <name>Human Readable Name for this Mirror.</name>
            <url>http://210.93.182.16:80/repository/maven-public/</url>
        </mirror>
    </mirrors>
</settings>

maven의 설정을 사용하지 않는 경우 프로젝트의 pom.xml에 아래와 같이 기록하여 사용가능합니다.

아래 방식으로 사용하기를 권장한다.

<project>
    <!-- ... -->
    <repositories>
        <repository>
            <id>chamomile</id>
            <name>chamomile</name>
            <url>https://chamomile.lotteinnovate.com/repo/</url>
        </repository>
    </repositories>
</project>

간혹 서버에서 라이브러리를 다운로드가 되지 않고, 시간이 오래 걸리는 경우가 있는데, 그런 경우 종료 후 다시 다운로드를 수행합니다.

  • mvn -U clean package (Force Update of Snapshots/Release)
  • mvn -o clean package (offline 모드)

개발/운영 환경 별 설정 파일의 내용을 변경하고 싶어요

개발환경 혹은 운영환경 별 환경 설정(e.g. 데이터베이스 접속 정보)에 대한 내용이 다르며, 아래와 같은 형태로 환경별 배포가 가능하다.

  1. maven profile
  2. spring profile
  3. java 환경 변수 (vm arguments)

대부분의 요건은 코드 변경 없이 배포 실수 등 이슈 핸들링 목적이므로 Maven의 profile 을 사용해서 빌드 환경을 구성하는 것을 선택

maven profile

local, development, test, staging, production 등 deploy 환경에 따라 달라져야할 정보와 패키징 될 리소스 파일을 maven profile을 이용하여 build 시에 구성할 수 있다.

제약사항: 배포 환경에 따라 새로 빌드 해야 한다. jenkins 스크립트를 통해 빌드 자동화 및 빌드 파일을 관리

  1. pom.xml 파일에 profile 관련 properties 추가 및 기본 값 설정
<properties>
    <environment>dev</environment>
</properties>
  1. pom.xml 파일에 profile 선언

    배포 파일 및 리소스 파일 관리를 위한 id 를 지정하여 사용

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <environment>dev</environment>
        </properties>
    </profile>
    <profile>
        <id>test</id>
        <properties>
            <environment>test</environment>
        </properties>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <environment>prod</environment>
        </properties>
    </profile>
    ...
    <!-- 환경에 맞는 pofile id 를 구성 -->
</profiles>
  1. pom.xml 파일에 build 엘리먼트에 resources 디렉토리 경로를 변경
<build>
    <resources>
        <resource>
               <directory>src/main/resources-${environment}</directory>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
        </resource>
        <!--
        <testResource>
            <directory>src/test/resources-${envoronment}</directory>
        </testResource>
        -->
    </resources>
</build>

빌드 시 resources 적용 순서

  • src/main/resources-${environment} 디렉토리의 데이터의 내용이 target/classes로 복사

  • src/main/resources 의 내용이 target/classes로 복사

    동일한 이름의 파일이 존재하는 경우 복사되지 않음

    즉, src/main/resources-dev/application.properties의 파일이 복사된 경우 src/main/resources/application.properties 파일은 복사되지 않음.

  1. maven package 수행시 -P 옵션을 통해 프로파일로 패키징
  • mvn package -P dev

  • mvn package -P local

  • mvn package -P prod

  1. target의 출력물 (.war / .jar) 를 열어서 deploy 환경에 맞는 리소스가 포함되었는지 확인

캐모마일과 연관이 없는 기본적인 사용 예시로 구체적인 설정 파일 및 설정 값 등은 프로젝트에서 확인하여 사용 CI/CD 도구를 통해서 자동화하고 프로파일별 배포파일을 관리하는 것을 활용 (Jenkin 등)

부트로 개발 된 프로젝트를 war 로 패키징하여 WAS에 배포하고 싶습니다.

WAS로 배포가능한 war로 패키징 하려면 아래 링크 내용에 나와있는 것처럼 일부 소스 수정이 필요합니다.

https://mkyong.com/spring-boot/spring-boot-deploy-war-file-to-tomcat/

변경 내용

  • XXXApplication.java
  • pom.xml

사용자 인증 시 캐모마일 테이블이 아닌 다른 테이블을 사용하고 싶다.

기본적으로는 캐모마일의 사용자 테이블을 사용하기를 권장한다. 하지만 이미 사용자의 테이블이 존재하거나, 혹은 캐모마일의 사용자 테이블을 사용하지 못하는 경우 아래와 같이 설정을 변경하여 적용 가능하다.

src/main/resources/spring/context-security.xml 파일의 내용 중 JdbcUserDetailsService의 설정을 변경한다.


<beans:bean id="JdbcUserDetailsService" class="net.lotte.chamomile.security.userdetails.JdbcUserDetailsService" >
    <beans:property name="dataSource" ref="dataSourceFactoryBean" />
    <beans:property name="usersByUsernameQuery" value="sql query" /> <!-- optional -->
    <beans:property name="authoritiesByUsernameQuery" value="sql query" />  <!-- optional -->
    <beans:property name="groupAuthoritiesByUsernameQuery" value="sql query" />  <!-- optional -->
</beans:bean>
  • usersByUsernameQuery (optional) : 입력받은 사용자 아이디로 사용자를 조회하는 쿼리를 설정한다.

    • 쿼리의 결과는 기본적으로 6개의 항목을 조회하고 있다. (alias를 활용하여 아래 이름으로 반환한다.)
    • userId(아이디), userPassword(비밀번호), enabled(활성화여부), accountNonExpired(계정이만료되지 않았는지), accountNonLocked(계정이 잠겨있지 않았는지), credentialsNonExpired(비밀번호가 만료되지 않았는지): 순서는 변경되어서는 안된다.
    • 예) select userId, userPassword, enabled, accountNonExpired, accountNonLocked, credentialsNonExpired from {your table} where {start_date} < ? and {end_date} > ? and userId = ?
  • authoritiesByUsernameQuery (optional) : 입력받은 사용자 아이디에 부여 된 권한 목록을 조회하는 쿼리를 설정한다.

    • 쿼리의 결과는 기본적으로 2개의 항목을 조회하고 있다.
    • userId(아이디), roleId(권한): 순서가 변경되어서는 안된다.
  • groupAuthoritiesByUsernameQuery (optional) : 사용자가 속한 그룹의 권한 목록을 조회하는 쿼리를 설정한다.

    • 쿼리의 결과는 기본적으로 3개의 항목을 조회하고 있다.
    • userId(아이디), groupId(그룹), roleId(권한): 순서가 변경되어서는 안된다.

참고로 인가(접근제어)에 관한 정보도 동일하게 다른 곳에서 정보를 가져와 사용가능하다.

<beans:bean id="securedUrlResourceService" class="net.lotte.chamomile.security.access.secured.url.SecuredUrlResourceService">
    <beans:property name="dataSource" ref="dataSourceFactoryBean" />
    <beans:property name="rolesQuery" ref="sql query" /> <!-- optional -->
</beans:bean>    

rolesQuery : URL 리소스에 대한 권한 정보를 조회하는 쿼리를 설정한다.

  • 기본적으로 url(리소스 url 정보), authority(권한)을 조회하게 되어 있다.
  • 순서와 명칭이 동일해야 한다.

부트에서는 아래와 같이 변경가능하다.

@Configuration
protected static class CustomizingSuccessAndFailureHandlerConfiguration
    extends net.lotte.chamomile.autoconfiguration.SecurityAutoConfiguration {

    @Bean
    @Override
    public JdbcUserDetailsService jdbcUserDetailsService(DataSource dataSourceFactoryBean) throws Exception {
        JdbcUserDetailsService jdbcUserDetailsService = new JdbcUserDetailsService();
        jdbcUserDetailsService.setDataSource(dataSourceFactoryBean);
        jdbcUserDetailsService.setUsersByUsernameQuery("sql query");
        jdbcUserDetailsService.setAuthoritiesByUsernameQuery("sql query");
        jdbcUserDetailsService.setGroupAuthoritiesByUsernameQuery("sql query");
        return jdbcUserDetailsService;
    }

    @Bean
    @Override
    public SecuredUrlResourceService securedUrlResourceService(DataSource dataSourceFactoryBean) {
        SecuredUrlResourceService securedUrlResourceService = new SecuredUrlResourceService();
        securedUrlResourceService.setDataSource(dataSourceFactoryBean);
        securedUrlResourceService.setRolesQuery("sql query");
        return securedUrlResourceService;
    }

    // ...
}

캐모마일에서 제공하는 시큐리티를 사용하고 싶지 않다.

캐모마일에서 제공하는 Security 관련 기능을 사용하지 않고자 하는 경우 캐모마일 Security에 관련 된 설정을 제거하면 된다.

솔루션 기반으로 프로젝트 진행 시, 인증 / 인가에 대한 모듈이 별도로 존재하는 경우가 있는 경우에 적용가능한 형태입니다. 그룹보안성심의를 고려하여 가급적이면 사용해 주십시요.

다만, 캐모마일에서는 기본적으로 Spring Security 기반의 기능들을 제공하고 있으며, 아래 기능들은 Security로 부터 사용자에 대한 정보를 획득하여 처리되도록 구성된다.

  • 이력 데이터 적재 시
  • 예외 정보 적재 시
  • 거래추적 데이터 적재 시
  • 메뉴 정보 로드 시
  • 사용자별 로그레벨 변경 시
  • 모바일 동시 사용자 제어 시
  • 모바일 통계 데이터 적재 시

위와 같은 기능들이 정상적으로 동작할 수 있도록 아래와 같은 항목을 변경한다.

  1. 로그인 시 사용자 정보 생성
  2. 매 요청 시 세션으로 부터 사용자 정보 로드
  3. 시큐리티 설정 제거

로그인 시 사용자 정보 생성

/* 당신의 인증 모듈의 인증이 성공한 시점에 Authentication을 만들어서 적재 */
private void onAuthenticationSuccess() {
    Object principal = "your user name";
    UserDetails credentials = new UserDetails() {
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return null;
        }
        @Override
        public String getPassword() {
            return null;
        }
        @Override
        public String getUsername() {
            return "your user name";
        }
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
        @Override
        public boolean isEnabled() {
            return true;
        }
    };

    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(principal, credentials, credentials.getAuthorities());
    // UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(credentials, credentials, credentials.getAuthorities());
    SecurityContext securityContext = SecurityContextHolder.getContext();
    securityContext.setAuthentication(authentication);
}

매 요청 시 세션으로 부터 사용자 정보 로드

매 요청 시 세션으로 부터 사용자 정보를 로드하기 위하여 기존 web.xml에 설정 된 Security 설정을 변경 한다.

변경 전

<!-- spring security -->
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filterclass>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

변경 후

<filter>
    <filter-name>securityContextPersistenceFilter</filter-name>
    <filterclass>org.springframework.security.web.context.SecurityContextPersistenceFilter</filter-class>
</filter>
    <filter-mapping>
    <filter-name>securityContextPersistenceFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

시큐리티 설정 제거

시큐리티 설정은 context-security.xml을 제거한다.

UI 어댑터를 사용하는 경우 context-uiadapter-security.xml 파일도 제거한다.

추가적으로 context-servlet.xml 파일에 존재하는 component-scan 시 exclude-filter를 추가한다.

<context:component-scan base-package="net.lotte.sample, net.lotte.chamomile.api">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="assignable" expression="net.lotte.chamomile.api.jscomponent.JscompoentController" />
</context:component-scan>

부트를 사용하는 경우 chamomile-security-starter 의존성을 제거한다.

넥사크로와의 연동은 어떻게 되는지

타 프레임워크에서 상용 UI 솔루션과 연동을 위한 모듈을 제공하나, 해당 프레임워크에 종속적인 형태로 개발이 된다.

Chamomile에서는 프레임워크에 대한 종속성을 탈피하기 위해 Spring MVC에서 제공하는 기술들을 이용하여 상용 UI 솔루션과 연동할 수 있는 방법을 제시한다.

즉, 다시말해 백엔드에서 개발되어지는 코드 자체가 프레임워크에 대한 종속성, 상용 UI 솔루션에 대한 종속성이 제거된다. 상용 UI 솔루션을 사용하지 않는 경우에도 동일한 코드로 서비스를 제공할 수 있다. (단, 예외적인 경우도 존재한다.)

지원하는 상용 UI 솔루션은 아래와 같다.

  1. nexacro (투비소프트)
  2. xplatform (투비소프트)
  3. websquare (인스웨이브시스템즈)

Spring MVC에서 제공하는 기술들을 그대로 사용할 수 있도록 구성된다.

image-20200911093232320

서버에서의 개발 방식은 기존 웹 개발과 동일한 형태로 개발을 할 수 있으며, 벤더사의 데이터 타입에 맞게 primitive 타입 혹은 pojo 형태의 데이터로 자동 변환된다.

상세한 내용은 Web_개발가이드UI어댑터 부분을 참고한다.

간단한 실습

프로젝트 생성

먼저 File -> New -> Project를 선택한 후 Chamomile Framework -> Create Chamomile Project를 선택한 후 Next를 클릭한다.

아래와 같이 창이 생성되면 값을 입력해준다.

image-20220404172655404

Base package를 net.lotte.sample로 지정한다.

이후 버전에서 변경되지 않는 부분은 패치될 예정입니다.

생성 된 프로젝트는 화면 예제를 포함하고 있으며, 컴파일 된 넥사크로 소스도 포함한다.

기본적으로 생성 된 소스는 http://localhost:8088/chamomile-samples-nexacro17 로 구성되어 있기 때문에 서버에 배포 시 portcontext를 변경하여 적용한다.

image-20220404172743203

eclipse의 servers 뷰에서 image-20220404172905594 를 더블클릭하면 서버 설정 화면이 나오고, port8088로 변경한다.

image-20220404173014540

이후 servers 뷰에서 tomcat을 선택하여 우클릭 하여 Add and Remove...를 선택하여 서버에 프로젝트를 추가한다.

image-20220404173102240

좀전에 만든 edu-nexacro를 추가한 후

image-20220404173204411

서버를 실행한다.

image-20220404173249349

혹시나 아래와 같은 오류가 발생하는 경우

Caused by: java.lang.IllegalArgumentException: Pointcut is not well-formed: expecting 'illegal identifier start (#)' at character position 29
(execution(* ###BASEPACKAGE###..*ServiceImpl.*(..)) || execution(* net.lotte.chamomile.core.servlet.mvc.method.RequestMappingInfoSynchronization.*(..)))

chmm.txPointcut.expression=(execution(* net.lotte.sample..*ServiceImpl.*(..)) or execution(* net.lotte.chamomile.core.servlet.mvc.method.RequestMappingInfoSynchronization.*(..)))

src/main/resources/application.properties 파일의 jdbc 설정 값과 txPointcut의 값을 변경해 준다. (기존 프로젝트에서 복사)

chmm.jdbc.driverClassName=net.sf.log4jdbc.DriverSpy
chmm.jdbc.jdbc-url=jdbc:log4jdbc:mariadb://127.0.0.1:3306/chamomile?autoReconnect=true&serverTimezone=UTC
chmm.jdbc.username=root
chmm.jdbc.password=ldcc!2626
chmm.jdbc.maximumPoolSize=8
chmm.jdbc.connectionTimeout=30000
chmm.jdbc.maxLifetime=1800000

chmm.txPointcut.expression=(execution(* net.lotte.sample..*ServiceImpl.*(..)) or execution(* net.lotte.chamomile.core.servlet.mvc.method.RequestMappingInfoSynchronization.*(..)))

이 후 버전에는 패치가 됩니다.

서버가 정상적으로 로드 된 후 http://localhost:8088/chamomile-samples-nexacro17 접속하여 넥사크로와의 데이터 연동이 정상적으로 이루어 지는지 테스트를 수행한다.

image-20220404173948219

requestParam 테스트는 실패하도록 구성되어져 있습니다. @RequestParam을 사용할 때에는 primitive 타입만 선언하여 사용할 수 있습니다.

//fail
@RequestMapping(value="/handleRequestParam")
public ModelAndView handleRequestParam(
    @RequestParam("addr") Address addr
    , @RequestParam("infoList") List<Address> infoList) {

넥사크로 기반으로 프로젝트를 진행하는 경우 투비소프트에서 진행하는 교육 수강하기를 권장한다.

넥사크로에서의 예외 처리는 어떻게 하나요?

캐모마일은 UiAdapter를 통해 기본적인 예외를 처리하고 있으며,

nexacro 연동 시 에러 코드는 -1로 고정 된 값을 반환하고 있습니다.

<Parameter id="ErrorCode" type="string">-1</Parameter> 
<Parameter id="ErrorMsg" type="string">An Error Occured.... 

인증 및 인가 오류 시

  • http status 200, ErrorCode에 -401, -403으로 응답하고 있습니다.

벤더사의 ErrorCode 값을 변경하여 UI에서 추가적인 처리를 하고자 하는 경우 NexacroException을 이용하여 ErrorCode와 ErrorMsg를 변경할 수 있습니다.

추가적으로 nexacro platform에서의 예외 처리는 아래 2군데에서 처리 가능합니다.

  • transaction의 callback (ErrorCode 처리)
  • ADL의 onError (http status에 대한 처리)

admin에서 설정 혹은 DB에서 직접 데이터를 변경하였는데, 잘 동작하지 않습니다.

캐모마일은 기본적으로 데이터를 캐시하여 사용중에 있습니다. (어드민에서 관리되는 데이터들이 캐시 된다.)

예를 들어, 공통코드에 대한 정보, 다국어에 대한 정보, 리소스 보안에 관련한 정보 등

DB에서 데이터를 직접 변경하거나, 내장 Redis와의 연결이 원활하지 않을 경우 변경 된 데이터가 반영되지 않는 현상이 가끔 발생합니다.

그럴때에는 캐모마일 어드민의 시스템 설정 > 캐시관리 영역에서 캐시 초기화를 해주시면 정상적으로 동작합니다.

에러가 났을때 에러는 어떻게 봐야 하는가?

어플리케이션을 개발하는 경우 다양한 예외를 만날 수 있습니다.

에러가 발생하는 경우 로그 내역을 확인해야 합니다.

에러의 정보는 console 혹은 파일에 기록 되며 아래와 같은 형태로 구성됩니다.

; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: (conn=375) Unknown column ‘DOC_IDs’ in ‘field list’ at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:235) ~[spring-jdbc-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72) ~[spring-jdbc-5.2.8.RELEASE.jar:5.2.8.RELEASE] at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:88) ~[mybatis-spring-2.0.4.jar:2.0.4] at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440) ~[mybatis-spring-2.0.4.jar:2.0.4] … at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.37.jar:9.0.37] at java.lang.Thread.run(Thread.java:748) [?:1.8.0_265] Caused by: java.sql.SQLSyntaxErrorException: (conn=375) Unknown column 'DOC_IDs' in 'field list' at org.mariadb.jdbc.internal.util.exceptions.ExceptionMapper.get(ExceptionMapper.java:242) ~[mariadb-java-client-2.4.4.jar:?] at org.mariadb.jdbc.internal.util.exceptions.ExceptionMapper.getException(ExceptionMapper.java:171) ~[mariadb-java-client-2.4.4.jar:?] … at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_265] at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:426) ~[mybatis-spring-2.0.4.jar:2.0.4] … 185 more Caused by: java.sql.SQLException: Unknown column 'DOC_IDs' in 'field list' at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.readErrorPacket(AbstractQueryProtocol.java:1594) ~[mariadb-java-client-2.4.4.jar:?] at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.readPacket(AbstractQueryProtocol.java:1453) ~[mariadb-java-client-2.4.4.jar:?] at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.getResult(AbstractQueryProtocol.java:1415) ~[mariadb-java-client-2.4.4.jar:?] at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.executeQuery(AbstractQueryProtocol.java:289) ~[mariadb-java-client-2.4.4.jar:?] at org.mariadb.jdbc.ClientSidePreparedStatement.executeInternal(ClientSidePreparedStatement.java:221) ~[mariadb-java-client-2.4.4.jar:?] … at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:426) ~[mybatis-spring-2.0.4.jar:2.0.4] … 185 more

간단하게 OKKY – 초보 개발자를 위한 스택트레이스 읽는 법를 참고

예외의 정보를 바탕으로 어디에서, 어떠한 예외가 발생했고, 예외의 내용은 무엇인지 찾을 수 있어야 한다.

로그 설정은 어떻게 변경할 수 있는가

캐모마일에서 로그는 log4j2를 이용하여 처리되고 있습니다.

이력 데이터 적재 시 성능 문제로 비동기로거를 사용하여 데이터를 적재합니다.

로그 파일은 src/main/resources/log4j2.xml 파일에 기록되어져 있으며, Rewrite Appender를 이용하여 로그 마스킹 기능을 제공합니다.

로컬 개발환경에서는 아래와 같이 Loggers의 Root 레벨을 DEBUG로 설정하여 사용하시길 권장드리며, 개발서버 및 운영 서버에서는 INFO로 변경하여 사용하시길 권장 드립니다.

<Configuration status="error" packages="net.lotte.chamomile.core.log.mask.filter" monitorInterval="1">

    <Loggers>
        <Root level="INFO">
               <AppenderRef ref="maskDailyRollingFile" /> 
        </Root>
    </Loggers>
</Configuration>

Root의 레벨은 명시 된 Logger를 제외한 로거들에 대한 레벨을 설정한다.

그리고 세부적으로 Logger 들에 대한 레벨을 변경하고자 하는 경우 아래와 같이 각 로거들의 레벨을 변경하여 사용할 수 있습니다.

<!-- Application Loggers -->
<Logger name="net.lotte.chamomile" level="DEBUG" additivity="false">
    <AppenderRef ref="maskDailyRollingFile" />
</Logger>
<Logger name="net.lotte.chamomile.security.util.matcher.SerializableAntPathRequestMatcher" level="INFO" additivity="false">
    <AppenderRef ref="maskDailyRollingFile" />
</Logger>

<!-- 3rdparty Loggers -->
<Logger name="org.springframework.security" level="DEBUG" additivity="false">
    <AppenderRef ref="maskDailyRollingFile" />
</Logger>

아래 로거들은 이력데이터를 DB에 적재하기 위한 용도로 사용되며, TRACE인 경우에만 데이터가 적재 된다.

 <!-- Method Trace Logger -->
<AsyncLogger name="UserLoginLogger" level="TRACE" additivity="false"/>
<AsyncLogger name="MethodTraceLogger" level="TRACE" additivity="false"/>
<!-- Transaction Tracer Logger -->
<AsyncLogger name="TransactionTraceLogger" level="TRACE" additivity="false"/>