ExtJS를 얼마만에 만져 보는건지..

2014년 ~ 2018년 까지 해보고 그 이후로 처음 해보는 것 같다.

 

이번에 한 업체에서 간단한 프로젝트를 맡겼고, 디자이너와 퍼블리셔, 기획 등이 없는 상황이라

ExtJS를 택하게 되었다. 가장 큰 장점은 UI Component 가 너무 잘 되어있다.

 

GPL 버전은 공식 오픈소스 버전으로 개발에 마음껏 사용하여도 좋다.

단, 상업적으로 이용할 시 소스는 공개하게 되어있다. 유의하길 바라며...

Sencha에서 ExtJS GPL 버전을 더 이상 지원을 안하나?

라이센스 구매 유도로 다 변경되어 있고,

7.0 버전 이후로 CMD도 GPL도 찾아보기도 힘들며 다운받기 어렵게 해놓았다.

 

우선, 작성일자 기준으로 소개를 해보겠다.

GPL 버전은 아래에서 받았다.

https://cdn.sencha.com/ext/gpl/ext-7.0.0-gpl.zip

 

그리고, ExtJS는 JS 라이브러리로 초기에는 빌드를 해야, 정상적으로 사용할 수 있다.

Sencha에서 지원하는 CMD로 해당 Package를 sdk 설치 해줘야 한다.

일반 소스가 아닌, ExtJS 라이브러리를 수정할 경우 해당 CMD로 계속 빌드 해줘야 한다.

 

CMD는 아무리 찾아도 받아볼 수 없어, Sencha에서 Trial 버전을 신청한 후 다운 받았다.

그럼 최신 CMD를 받는데, 그게 7.8버전이다.

 

그럼 GPL 버전을 압축 해제한 후 sencha cmd로 해당 경로에 접속 한 다음 아래 명령어를 입력해 보자.

sencha -sdk /path/to/sdk generate app MyApp /path/to/myapp

 

/path/to/sdk 는 압축을 해제 한 GPL 경로
MyApp은 프로젝트 명

/path/to/myapp 는 압축 해제한 GPL package를 풀 경로를 뜻한다.

 

완료가 된다면, 프로젝트가 풀어진 경로에서 

sencha app watch를 해보자

localhost:1841 이 서비스가 시작 될 것이며 해당 페이지를 띄어보면 아래와 같은 화면이 나올 것이다.

 

Ext JS SDK Build 최초 화면

 

HttpClient 공통 Util을 소개합니다.

 

package x.util;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Map;

/**
 * packageName    : x.util;
 */
public class HttpClientUtil {

    private static final HttpClient httpClient = HttpClient.newHttpClient();

    private static final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * Get t.
     *
     * @param <T>          the type parameter
     * @param url          the url
     * @param headers      the headers
     * @param responseType the response type
     * @return the t
     * @throws IOException          the io exception
     * @throws InterruptedException the interrupted exception
     */
    public static <T> T get(String url, Map<String, String> headers, Class<T> responseType) throws IOException, InterruptedException {
        HttpRequest.Builder builder = HttpRequest.newBuilder().uri(URI.create(url)).GET();
        addHeaders(builder, headers);
        HttpResponse<String> response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofString());
        return objectMapper.readValue(response.body(), responseType);
    }

    /**
     * Post t.
     *
     * @param <T>          the type parameter
     * @param url          the url
     * @param requestBody  the request body
     * @param headers      the headers
     * @param responseType the response type
     * @return the t
     * @throws IOException          the io exception
     * @throws InterruptedException the interrupted exception
     */
    public static <T> T post(String url, Object requestBody, Map<String, String> headers, Class<T> responseType) throws IOException, InterruptedException {
        String json = objectMapper.writeValueAsString(requestBody);
        HttpRequest.Builder builder = HttpRequest.newBuilder().uri(URI.create(url)).POST(HttpRequest.BodyPublishers.ofString(json));
        addHeaders(builder, headers);
        HttpResponse<String> response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofString());
        return objectMapper.readValue(response.body(), responseType);
    }

    /**
     * Put t.
     *
     * @param <T>          the type parameter
     * @param url          the url
     * @param requestBody  the request body
     * @param headers      the headers
     * @param responseType the response type
     * @return the t
     * @throws IOException          the io exception
     * @throws InterruptedException the interrupted exception
     */
    public static <T> T put(String url, Object requestBody, Map<String, String> headers, Class<T> responseType) throws IOException, InterruptedException {
        String json = objectMapper.writeValueAsString(requestBody);
        HttpRequest.Builder builder = HttpRequest.newBuilder().uri(URI.create(url)).PUT(HttpRequest.BodyPublishers.ofString(json));
        addHeaders(builder, headers);
        HttpResponse<String> response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofString());
        return objectMapper.readValue(response.body(), responseType);
    }

    /**
     * Delete t.
     *
     * @param <T>          the type parameter
     * @param url          the url
     * @param headers      the headers
     * @param responseType the response type
     * @return the t
     * @throws IOException          the io exception
     * @throws InterruptedException the interrupted exception
     */
    public static <T> T delete(String url, Map<String, String> headers, Class<T> responseType) throws IOException, InterruptedException {
        HttpRequest.Builder builder = HttpRequest.newBuilder().uri(URI.create(url)).DELETE();
        addHeaders(builder, headers);
        HttpResponse<String> response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofString());
        return objectMapper.readValue(response.body(), responseType);
    }

    private static void addHeaders(HttpRequest.Builder builder, Map<String, String> headers) {
        if (headers != null) {
            headers.forEach(builder::header);
        }
    }
}

 

Map<String, String> headers 은 Header

Object requestBody 는 parameter 

Class<T>의 responseType은 응답값을 받고자하는 class 타입을지정

   

 

사용법은 아래와 같음

public Map<String, Object> httpClientTest(@PathVariable String method) throws IOException, InterruptedException {
        Map<String, Object> response;
        if ("get".equals(method)) {
            response = HttpClientUtil.get(
            "https://jsonplaceholder.typicode.com/posts/1",
                Map.of("Content-type", "application/json; charset=UTF-8"),
                Map.class
            );
        } else
        if ("post".equals(method)) {
            Map<String, Object> map = new HashMap<>();
            map.put("title", "foo");
            map.put("body", "bar");
            map.put("userId", 1);
            response = HttpClientUtil.post(
                "https://jsonplaceholder.typicode.com/posts",
                map,
                Map.of("Content-type", "application/json; charset=UTF-8"),
                Map.class
            );
        } else
        if ("put".equals(method)) {
            Map<String, Object> map = new HashMap<>();
            map.put("id", 1);
            map.put("title", "foo");
            map.put("body", "bar");
            map.put("userId", 1);
            response = HttpClientUtil.put(
                "https://jsonplaceholder.typicode.com/posts/1",
                map,
                Map.of("Content-type", "application/json; charset=UTF-8"),
                Map.class
            );
        } else {
            Map<String, String> map = new HashMap<>();
            response = HttpClientUtil.delete(
            "https://jsonplaceholder.typicode.com/posts/1",
                map,
                Map.class
            );
        }
        return null;
    }

 

 

Gradle 기준 아래 의존성 추가

dependencies {
    implementation("com.fasterxml.jackson.core:jackson-databind:2.17.0")
}

 

 

https://jsonplaceholder.typicode.com

 

JSONPlaceholder - Free Fake REST API

{JSON} Placeholder Free fake and reliable API for testing and prototyping. Powered by JSON Server + LowDB. Serving ~3 billion requests each month.

jsonplaceholder.typicode.com

 

위 사이트는 REST API 테스트를 제공해주는 사이트입니다.

호출 이후 결과도 HTTP Method 에 따라 가이드가 있으니 이용하세요!  

이건 저만의 설정인데요

^^.. 설명은 생략해볼게요

 

 

log4jdbc.log4j2.properties

log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator
log4jdbc.dump.sql.maxlinelength=0


pom.xml

<!-- Custom maven :S -->
	<dependency>
		<groupId>org.bgee.log4jdbc-log4j2</groupId>
		<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
		<version>1.16</version>
	</dependency>
<!-- Custom maven :E -->


datasource

driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
jdbc-url: jdbc:log4jdbc:mariadb


log4j.xml

<configuration>
	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
		<encoder>
			<pattern>%green(%d{yyyyMMdd HH:mm:ss.SSS}) %cyan([%thread]) %highlight(%-3level) %yellow(%logger{5}) - %white(%msg) %n</pattern>
		</encoder>
	</appender>

	<logger name="jdbc" level="OFF"/>
	<logger name="jdbc.sqlonly" level="INFO"/>
	<logger name="jdbc.sqltiming" level="INFO"/>
	<logger name="jdbc.audit" level="OFF"/>
	<logger name="jdbc.resultset" level="OFF"/>
	<logger name="jdbc.resultsettable" level="INFO"/>
	<logger name="jdbc.connection" level="OFF"/>

	<root level="INFO">
		<appender-ref ref="STDOUT" />
	</root>
</configuration>


sqlSessionFactory

/* Custom DataSource : S */
	Log4jdbcProxyDataSource log4jdbcProxyDataSource = new Log4jdbcProxyDataSource(dataSource);
	Log4JdbcCustomFormatter log4JdbcCustomFormatter = new Log4JdbcCustomFormatter();
	log4JdbcCustomFormatter.setLoggingType(LoggingType.MULTI_LINE);
	log4JdbcCustomFormatter.setSqlPrefix("\nSQL ::::: ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ\n");
	log4jdbcProxyDataSource.setLogFormatter(log4JdbcCustomFormatter);
	final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
	sessionFactory.setDataSource(dataSource);
	sessionFactory.setDataSource(log4jdbcProxyDataSource);

/* Custom DataSource : E */

SSL 인증서 만료일이 도래하여 교체작업을 하였다.
 
서버는 Apache 였으며 총4개의 파일을 교체하고, Apache의 설정파일에 경로를 변경하는 작업이었다.

cert_domain.crt
domain.key
rootca_domain.crt
subca_domain.crt

 
모두 변경을 완료한 이후 apache를 재시작하였으나, Error 발생 !!
 
로그를 확인해보니 아래와 같이 기록이 되있었다.

AH02241: Init: Unable to read server certificate from file /xxx/domain.key
SSL Library Error: error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag
SSL Library Error: error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error (Type=X509)
AH02312: Fatal error initialising mod_ssl, exiting.

 
Key 파일을 열어보니 

key 파일

 
위와 같이 암호화된 문자로 시작되었으며 이는 SSL 인증서를 적용하는 정상적인 상태가 아니었다.
해당 파일의 문자열은 복호화가 된 상태로 있어야 한다.
 
복호화방법은 아래와 같다.

openssl rsa -in [복호화 할 파일명(암호화 된 파일명)] -out [복호화 될 파일명]
Enter pass pharse for xxxx: 비밀번호 입력

 
위와 같이 복호화 이후 Apache를 재시작 하니 정상 작동 하였다.

+ Recent posts