오늘 Spring  Boot 에 Logback 설정을 하다가 profile 에 따라 분리의 필요성을 느껴 찾아보고 적용하였습니다.

 

우선 logback.xml 파일은 spring extention 을 사용할 수 있게 logback-spring.xml 로 이름을 짓습니다.

 

그리고 2가지 경우로 나누어 설정해 보았습니다.

 

첫번째로 미리 appender 를 선언하고 호출하여 쓰는 경우.

 

<property name="LOG_PATH" value=""/>
<property name="LOG_FILE_NAME" value=""/>

 

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%-5level %d{HH:mm:ss.SSS} [%thread %F:%L] %method - %msg%n
</pattern>
</encoder>
</appender>

 

<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${LOG_FILE_NAME}.log</file>
<encoder>
<pattern>%-5level %d{HH:mm:ss.SSS} [%thread %F:%L] %method - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>60</maxHistory>
</rollingPolicy>
</appender>

 

<springProfile name="dev | stage">
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>

<appender-ref ref="ROLLING_FILE"/>

</root>
</springProfile>

 

이렇게 쓸경우 springProfile 에 따라 appender 가 중복되지 않고 호출만 하여 사용하면 되어 편리합니다.

하지만 로그 파일의 위치가 권한이 필요한 곳이라면 미리 appender 를 선언하고 해당 설정을 읽을때 파일에 접근, 생성하기 때문에 따로 권한을 줘야합니다. (제 경우에는 앱 실행당시 바로 access denied 가 떴었습니다.)

 

 

두번째로 springProfile 별로 선언하고 쓰는 경우.

 

<springProfile name="dev | stage">

<property name="LOG_PATH" value=""/>
<property name="LOG_FILE_NAME" value=""/>

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%-5level %d{HH:mm:ss.SSS} [%thread %F:%L] %method - %msg%n
</pattern>
</encoder>
</appender>

<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/${LOG_FILE_NAME}.log</file>
<encoder>
<pattern>%-5level %d{HH:mm:ss.SSS} [%thread %F:%L] %method - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/${LOG_FILE_NAME}.%d{yyyy-MM-dd}_%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>60</maxHistory>
</rollingPolicy>
</appender>

<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ROLLING_FILE"/>
</root>
</springProfile>

 

이렇게 쓸경우 중복은 되지만 환경에 따라 세부적인 설정을 변경하기도 좋고 파일 생성이 필요없는 환경에서는 쓸데없이 파일을 만드는 appender 에 접근하지 않아 파일을 만들지 않아서 좋았습니다.

 

 

 

 

 

'Programming > Spring' 카테고리의 다른 글

profiles 를 활용하여 설정 값 변경하기.  (0) 2018.08.13

어느날 코딩을 하던 도중 Listadd() 를 하였는데 에러가 발생하였다.

 

익셉션은 UnsupportedOperationException.

 

원인을 찾던 도중 해결에 결정적인 역활을 하는 글과 재밌는 것을 발견하였다.

 

우선 글은

 

https://stackoverflow.com/questions/5755477/java-list-add-unsupportedoperationexception

 

Java List.add() UnsupportedOperationException

I try to add objects to a List instance but it throws an UnsupportedOperationException. Does anyone know why? My Java code: String[] membersArray = request.getParameterValues('membe...

stackoverflow.com

인데 내용을 번역해보자면

모든 Listadd() 메서드를 구현, 지원하지 않으며 가장 일반적인 예로 Arrays.asList(); 같은 경우 고정된 사이즈의 Array 를 리턴한다.
그렇기에 사이즈를 수정할 수 없다.

 

이다.

나같은 경우 Arrays.asList(); 를 통해 만들어진 ArrayListadd 를 하고 있었다.

 

그래서 궁금한 마음에 Arrays  클래스를 확인해본 결과 우리가 흔히 사용하는 ArrayList 를 반환하는게 아니라 

 

Arrays 클래스 내부에 inner Class 로 구현해놓은 ArrayList 를 반환하고 있었다.

당연하게도 Arrays 클래스의 inner ClassArrayList 에는 사이즈 측정 및 조절을 위한 size 와 DEFAULT_CAPACITY 변수가 존재하지 않는다.

그리고 AbstractList 클래스를 상속받고 있어 상속받은 AbstractList 클래스의 add() 메서드는 존재하고 사용할 수는 있지만 


해당 AbstractList 클래스의 add() 메서드를  Override 하여 사용하지 않으면 해당 add() 메서드는  

throw new
UnsupportedOperationException(); 하도록 구현되어있기 때문에 UnsupportedOperationException 에러가 나게 된다.

 

 

 

3장. 함수 에 대한 의견 교환




이 글은 로버트C. 마틴이 지은 클린코드를 읽고 각 챕터에 대한 의견 교환 및 토론을 한 내용 입니다.

댓글로 의견 교환 및 토론은 환영합니다.




오늘은 함께 스터디하는 4명 중 2명이 일과 건강 문제로 스터디에 빠져서 나를 포함해 2명이서 스터디를 진행하였다.




: 

  • 더러운 함수는 이해가 안간다.
  • 함수를 작게 만들라는게 마음에 든다.
  • 함수를 한가지 일만 해야한다고 하는게 뇌리에 박힌다.
  • Switch 문을 사용할때 클린코드 규칙이 깨진다고 하는게 가장 중요한 이야기인것 같고 Switch 뿐만 아니라 if 문을 여러번 사용할때도 같다고 생각된다.
  • 나머지는 와닿지 않는다.
    • ) 함수인수 갯수, boolean 타입을 넘기는것 ..
  • 아직 경험이 별로 없어서 와닿지 않고 enum 크게 와닿진 않는다.




나: 

  • enum 오류 코드를 사용하는 것이 의존성 자석이고 그렇게 쓰지말라고 한 것 같은 경우는 기존의 exception 클래스를 상속받아서 새롭게 해당 예외만 생성하고 사용하면 된다는 말 같다.
  • Enum 사용할때 타입 같은 것을 정의하고 값을 DB에 저장하면 좋다고 생각한다.
    • string 이라는게 에러가 생길수 있는 구석이 많음, 예를 넘겨주거나 받을때 String 이면 kakaopay 인데 kakpay 같이 될수 있는데 이를 방지할수 있다.
  • 함수 챕터에서 로버트C. 마틴이 함수인수의 갯수에 대해 이야기한 것이 중요하다고 생각되는 이유는 함수 인수가 많으면 설계와 정의를 못한거라고 생각한다.
  • 알고리즘 문제를 풀더라도 객체지향적으로 설계하고 정의하면 함수 인수가 많아질 일은 없다고 생각한다.
    • 예를 들어 이진수 변환 문제가 나오면 나는 하나의 이진수 클래스로 만들어 이진수를 저장하는 것을 멤버변수로 만들고 변환하는 과정을 매서드로 만들 것이다.
  • 챕터에서 가장 중요한 말은우리가 함수를 만드는 이유는 개념을 다음 추상화 수준에서 여러단계로 나눠 수행하기 위해서가 아니던가인듯하다.
    • 말에 객체지향의 목표가 있다고 생각든다.
    • 예를 들어 함수에서 다음 함수로 넘어가거나 쪼갤때 단순히 줄을 나눠서 쪼개거나 그냥 인수를 그대로 넘기면 절차지향과 다름없다.
    • 함수를 쪼갤 , 혹은 구현 혹은 리팩토링할 함수를 추상화하고 함수에서 호출하는 다음 함수의 레벨(깊이, 레이어) 나눠줘야 한다고 생각한다.
  • 이는 단순히 함수뿐만 아니라 다른곳에도 적용된다고 생각한다.
    • 예를 들면 DDD 에서 도메인 레이어에서 인프라 레이어로 넘어가는데 이때에도 적용되는 말이라고 생각한다. 
    • 그래서 인프라 레이어에서의 변경이 일어나도 도메인 레이어에는 영향이 없도록 인프라 레이어를 사용, 호출하는 도메인 레이어에서는 인프라 레이어를 추상화(인터페이스화) 하는 것이 중요하다고 생각한다.

Spring Boot를 run 할 때 멈추는 경우


저에게 없던 기존의 Spring Boot 의 프로젝트를 실행해보기 위해 run 을 눌렀는데 아무런 반응이 없습니다.
아래와 같은 메세지만 출력이 되고 더이상 진행되지 않습니다.



[main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Included patterns for restart : []
[main] DEBUG org.springframework.boot.devtools.settings.DevToolsSettings - Excluded patterns for restart : [/spring-boot-starter/target/classes/, /spring-boot-autoconfigure/target/classes/, /spring-boot-starter-[\w-]+/, /spring-boot/target/classes/, /spring-boot-actuator/target/classes/, /spring-boot-devtools/target/classes/]
[main] DEBUG org.springframework.boot.devtools.restart.ChangeableUrls - Matching URLs for reloading : [file:/Users/a1101085/IdeaProjects/dsp/out/production/classes/, file:/Users/a1101085/IdeaProjects/dsp/out/production/resources/]



원인을 찾기 위해 열심히 검색한 결과 application.yml 혹은 logback-spring.xml 파일이 잘못되있을 경우 에러(error) 메세지 없이 실행이 안된다고 합니다.
제 경우에는 로그백에서 로그를 쌓기위한 디렉토리(directory) 가 없었는데 이를 만들려고 했으나 권한이 없어 만들지 못하고 그대로 실행을 멈춘 경우였습니다.
그래서 해당 디렉토리를 만들고 권한을 주니 해결.




참고:
https://stackoverflow.com/questions/45179385/spring-boot-devtools-restart-does-not-automatically-work-with-spring-boot-1-5-4


3.17. Parallel Execution

By default, JUnit Jupiter tests are run sequentially in a single thread. Running tests in parallel, e.g. to speed up execution, is available as an opt-in feature since version 5.3. To enable parallel execution, simply set thejunit.jupiter.execution.parallel.enabled configuration parameter to true, e.g. in junit-platform.properties(see Configuration Parameters for other options).

Once enabled, the JUnit Jupiter engine will execute tests on all levels of the test tree fully in parallel according to the provided configuration while observing the declarative synchronization mechanisms. Please note that the Capturing Standard Output/Error feature needs to enabled separately.

Parallel test execution is currently an experimental feature. You’re invited to give it a try and provide feedback to the JUnit team so they can improve and eventually promote this feature. 


3.17. Parallel Execution


기본적으로 JUnit Jupiter 테스트는 단일 스레드에서 순차적으로 실행됩니다. 테스트를 병렬로 실행할 때 실행 속도를 높이기 위해 5.3 버전부터 opt-in 기능을 사용할 수 있습니다. 병렬 실행을 사용하려면 junit-platform.properties 에 있는 junit.jupiter.execution.parallel.enabled configuration 매개 변수를 true로 설정하면됩니다. (다른 옵션에 대해서는 Configuration Parameters 참조)

JUnit Jupiter 엔진이 활성화되면 선언 된 동기화 메커니즘을 관찰하면서 제공된 구성에 따라 테스트 트리의 모든 수준에서 완전히 병렬로 테스트를 실행합니다. 표준 출력 / 오류 캡처 기능은 별도로 활성화해야합니다.






'스터디 > JUnit' 카테고리의 다른 글

[JUnit 5] 3.15. Test Templates  (0) 2018.12.19

[React Native] this.state.list.map is not a function


개인 프로젝트로 React Native 를 개발하던 도중 this.state.list.map is not a function 이라는 에러를 만났다.


금방 고칠 수 있었는데 .map() 은 Array 를 위한 함수로 그 외에 String 이나 다른 값이 들어가게 되면 에러를 내게 된다.



map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환합니다.


라고 한다.


그래서 this.state.list 에 주입할 Array 를 만드는 부분을 보니 데이터를 받아오는 url 주소가 잘못 되어있었다.






3.16. Dynamic Tests

The standard @Test annotation in JUnit Jupiter described in Annotations is very similar to the @Test annotation in JUnit 4. Both describe methods that implement test cases. These test cases are static in the sense that they are fully specified at compile time, and their behavior cannot be changed by anything happening at runtime. Assumptions provide a basic form of dynamic behavior but are intentionally rather limited in their expressiveness.

In addition to these standard tests a completely new kind of test programming model has been introduced in JUnit Jupiter. This new kind of test is a dynamic test which is generated at runtime by a factory method that is annotated with @TestFactory.

In contrast to @Test methods, a @TestFactory method is not itself a test case but rather a factory for test cases. Thus, a dynamic test is the product of a factory. Technically speaking, a @TestFactory method must return a StreamCollectionIterableIterator, or array of DynamicNode instances. Instantiable subclasses of DynamicNode are DynamicContainer and DynamicTestDynamicContainer instances are composed of a display name and a list of dynamic child nodes, enabling the creation of arbitrarily nested hierarchies of dynamic nodes. DynamicTest instances will be executed lazily, enabling dynamic and even non-deterministic generation of test cases.

Any Stream returned by a @TestFactory will be properly closed by calling stream.close(), making it safe to use a resource such as Files.lines().

As with @Test methods, @TestFactory methods must not be private or static and may optionally declare parameters to be resolved by ParameterResolvers.

DynamicTest is a test case generated at runtime. It is composed of a display name and an ExecutableExecutable is a @FunctionalInterface which means that the implementations of dynamic tests can be provided as lambda expressions or method references.

Dynamic Test Lifecycle
The execution lifecycle of a dynamic test is quite different than it is for a standard @Test case. Specifically, there are no lifecycle callbacks for individual dynamic tests. This means that @BeforeEach and @AfterEach methods and their corresponding extension callbacks are executed for the @TestFactorymethod but not for each dynamic test. In other words, if you access fields from the test instance within a lambda expression for a dynamic test, those fields will not be reset by callback methods or extensions between the execution of individual dynamic tests generated by the same @TestFactory method.

As of JUnit Jupiter 5.3.2, dynamic tests must always be created by factory methods; however, this might be complemented by a registration facility in a later release.

Dynamic tests are currently an experimental feature. Consult the table in Experimental APIs for details.




3.16. 다이나믹 테스트들


Annotations 에 설명 된 JUnit Jupiter의 표준 @Test annotation은 JUnit 4의 @Test annotation과 매우 유사합니다. 둘 다 테스트 케이스를 구현하는 메소드를 설명합니다. 이 테스트 케이스들은 컴파일 타임에 완전히 지정된다는 점에서 정적이며 런타임시 발생하는 모든 사항에 의해 그 동작이 변경 될 수 없습니다. Assumptions 은 동적 행동의 기본 형태를 제공하지만 표현의 한계가있다.


이러한 표준 테스트 외에도 완전히 새로운 종류의 테스트 프로그래밍 모델이 JUnit Jupiter에 도입되었습니다. 이 새로운 종류의 테스트는 @TestFactory 로 주석 처리 된 팩토리 메서드에 의해 런타임에 생성되는 동적 테스트입니다. @TestFactory 메소드는 @Test 메소드와는 달리 테스트 케이스가 아니라 테스트 케이스의 팩토리입니다. 따라서 동적 테스트는 팩토리의 제품입니다. 기술적으로 말해서 @TestFactory 메소드는 Stream, Collection, Iterable, Iterator 또는 DynamicNode 인스턴스의 배열을 반환해야합니다. DynamicNode 의 인스턴스화 가능한 서브 클래스는 DynamicContainer DynamicTest 입니다. DynamicContainer 인스턴스는 표시 이름과 동적 하위 노드 목록으로 구성되어 임의로 중첩 된 동적 노드 계층을 만들 수 있습니다. DynamicTest 인스턴스가 느리게 실행되므로 동적이고, 비 결정적인 테스트 케이스 생성이 가능합니다.


@TestFactory 에 의해 리턴 된 모든 스트림은 stream.close() 를 호출하여 적절히 닫히게되므로 Files.lines() 와 같은 리소스를 사용하는 것이 안전합니다.


@Test 메서드와 마찬가지로 @TestFactory 메서드는 private 또는 static이 아니어야하며 ParameterResolvers에서 확인할 매개 변수를 선택적으로 선언 할 수 있습니다.


DynamicTest는 런타임에 생성되는 테스트 케이스입니다. 그것은 표시 이름과 Executable로 구성됩니다. Executable@FunctionalInterface입니다. 즉, 동적 테스트의 구현을 람다 식 또는 메서드 참조로 제공 할 수 있습니다.


JUnit Jupiter 5.3.2에서 동적 테스트는 항상 팩토리 메소드로 작성해야합니다. 그러나 이것은 이후 릴리스의 등록 기능으로 보완 될 수 있습니다.





3.15. Test Templates

@TestTemplate method is not a regular test case but rather a template for test cases. As such, it is designed to be invoked multiple times depending on the number of invocation contexts returned by the registered providers. Thus, it must be used in conjunction with a registered TestTemplateInvocationContextProvider extension. Each invocation of a test template method behaves like the execution of a regular @Test method with full support for the same lifecycle callbacks and extensions. Please refer to Providing Invocation Contexts for Test Templates for usage examples.



3.15. 테스트 템플릿츠

@TestTemplate 매서드는 정기적인, 규칙적인 테스트 케이스가 아니고 정확히 말하자면 테스트 케이스들을 위한 템플릿 입니다. 그 때문에, 그것은 등록 프로 바이더가 돌려주는 호출 문맥의 수에 응해, 여러번 호출하도록 설계되고 있습니다. 따라서 그것은 반드시 등록TestTemplateInvocationContextProvider 의 확장과 함께 사용해야 합니다. 테스트 템플릿 메소드를 호출 할 때마다 동일한 라이프 사이클 콜백 및 확장을 완벽하게 지원하는 일반 @Test 메소드를 실행하는 것처럼 동작합니다. 사용 예제는 Providing Invocation Contexts for Test Templates 을 참조하십시오.


























'스터디 > JUnit' 카테고리의 다른 글

[JUnit5] 3.17. Parallel Execution  (0) 2018.12.26



[Node.js] Express에서 기본 Jade 템플릿 형식을 Html로 바꾸기

(Change the template system from Jade to HTML with Express)



 처음 express 를 통해 프로젝트 디렉토리를 만들경우 기본 템플릿 형식입니다.

Jade 를 처음 접하거나 쓰기 어려워하는 분들은 HTML 을 사용하고자 하는데 간단하게 기본 템플릿 형식을 바꾸는 방법이 있습니다.


출처 : https://ademirgabardo.wordpress.com/2016/03/04/how-to-change-the-template-system-from-jade-to-html-with-express/



This quick post is just to remember how to change the template engine on Express from Jade to HTML.

  1. Install the ejs view engine with the NPM:
    npm install ejs
  2. Set your template engine in app.js (or in your custom .js app file) as ejs
    app.engine('html', require('ejs').renderFile);
    app.set('view engine', 'html');
  3. In the route file ./routes/index.js  assign template variables like this
    exports.index = function(req, res){
    res.render('index', { title: 'Page title' });};
  4. Create the HTML files in the view folder /views.


2번 항목에서 app.js 파일을 보면 아래와 같이 기본 템플릿이 Jade 로 설정되어 있는 것을 볼 수 있습니다.



app.set('view engine', 'jade');



이를 지우거나 주석처리를 하고 2번을 진행하시면 됩니다.









profiles 를 활용하여 설정 값 변경하기.



스프링 개발을 하다보면 환경, 앱을 돌리고자 하는 목적에 따라 설정 값을 환경에 맞춰 변경해줘야 하는 경우가 생깁니다.

이럴 때 일일이 바꿀 필요 없이 편하게 설정 값을 변경하는 방법이 있습니다.


profiles 를 활용하는 방법입니다.





우선 환경에 따라 다른 설정 값을 가진 설정 파일을 여러개 만듭니다.

(저는 yml 을 사용했지만 properties를 사용하셔도 됩니다. )





그 후 위와 같이 Edit Configurations... 를 클릭합니다.





그 후 위 사진처럼 VM options 입력란-Dspring.profiles.active=적용하고자하는 환경 을 입력하고 앱을 run 시키면 설정한 환경의 설정 값을 가지고 있는 설정파일을 바탕으로 돌고 있는 것을 확인할 수 있습니다.





또는 터미널에서 실행시키고 싶다면 shell script 혹은 직접 위와같이 직접 쳐서 환경 설정 파일을 바꾸어 실행할 수 있습니다.




'Programming > Spring' 카테고리의 다른 글

Profile 에 따른 Logback 분리  (0) 2020.03.04

+ Recent posts