🧛🏻‍♀️ Big Sur 업데이트 하고

요즘 개발자들 중 많은 수가 맥북을 사용할 것이다. 얼마전 Mac OS 11 - Big Sur 정식 버전이 release 되었고, 업데이트를 보면 참을수 없는 유혹을 느끼는 나는 그 소식을 듣자마자 바로! 그것도 업무용 컴퓨터에! 설치해버렸다.
2~3년 전에는 Mac OS 업데이트 할때마다 버그도 많고 안정성도 예전같지 않아서 업데이트를 바로 설치하는 것을 꺼렸었는데, 근래에는 좀 나아진 모습을 보이는 것 같아서(어디까지나 주관적인 생각이다) 믿고 업데이트를 했다. 화사하고 iPad OS 같은 예쁜 모습에 만족도 잠시, 자잘한 버그들이 하나 둘 눈에 띄기 시작했다.

🤮 VSCode 의 Terminal에서 랙이 생긴다

다른 문제는 차치하고서라도, 회사에서 주로 쓰는 VSCode 사용시 터미널이 한번씩 반응을 멈췄다가 하는 문제였다. 처음에는 SpotLight 가 잡아먹는 cpu/ssd 부하가 좀 높은 편이길래 ‘아~ indexing 때문에 이렇게 버벅거리는구나…’ 했었지만, 며칠간 계속 써봐도 도통 나아질 기미가 보이지를 않았다. 사실 개발자는 검색을 얼마나 잘 하는지도 개발자의 실력 중 하나인데, 이런 부분은 어떻게 검색해야 하나 검색어부터 좀 난감해 하던 찰나, 이런 저런 검색어로 검색을 해보다 우연히 발견 했다.

오.. 역시 외국에서도 비슷한 문제를 겪고 있는 사람들이 많구나~

https://github.com/microsoft/vscode/issues/105446

오픈된 이슈 글을 보면 대략적으로 23초~24초마다 한번씩 spike가 튄다고 돼있다.

❓ 원인은?

Big Sur의 문제인지 VSCode를 실행할때 사용하는 electron의 문제인지는 명확히 모른다. But, Big Sur 에서 서명된 electron 앱의 child process가 lock 된다는 글을 봤을땐 Big Sur와 electron, 그리고 signing의 문제인 듯 하다.

👩🏻‍🔬 해결책은?

여기 👈🏻

1
codesign --remove-signature /Applications/Visual\ Studio\ Code.app/Contents/Frameworks/Code\ Helper\ \(Renderer\).app

Mac OS 의 codesign 프로그램으로 사인을 제거해주면 일단 해결된다. 일렉트론이 버전 업 하면서 이 문제를 해결할지 어쩔지는 모르겠지만.. 일단 터미널에서 위 명령을 실행한 뒤 VSCode를 완전히 재시작 하면 터미널 랙에서는 완전히 해방됐다.

Comment and share

그동안 이직하고 적응하느라 바빴다는 핑계로.. 글을 쓰지 않다가 정말 오랜만에 글을 하나 쓴다. 별로 대단히 길게 쓸 글은 아니고, 최근 개인 프로젝트를 시작하며 그동안 손에서 놓아두었던 java 개발을 다시 하려고 프로젝트 셋팅을 진행하다가 삽질한 경험이다.

swagger가 어느덧 3.0 버전이 나왔다길래 사용해볼까 싶어서 셋팅 하다 보니 뭔가 생각대로 되지 않아 짧게 셋팅하는 방법을 적어본다. 셋팅하는 방법은 #링크 를 참조 했다.

🌿 dependency 추가

필자는 gradle만 사용하므로 gradle 버전만 작성해놓겠다.

1
2
3
4
5
dependencies {
// ...
implementation "io.springfox:springfox-boot-starter:3.0.0"
// ...
}

swagger 3.0 오면서 편해졌다. springfox-boot-starter 하나만 추가 하면 얘가 필요한 dependency를 다 들고 있기 때문에 신경 쓸게 적다.

⚙ 설정파일 추가

사실 이렇게만 해도 spring boot app 구동 시에 자동으로 뜨긴 한다.
그렇지만 간단한 설정을 추가해 보자.

적당한 곳에 config 관련 패키지를 추가하고 SwaggerConfig.java 파일을 추가하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.deliwind.grams.configrations;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

@Configuration
public class SwaggerConfig {

@Bean
public Docket api() {
return new Docket(DocumentationType.OAS_30) // open api spec 3.0
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
}

🏃🏻‍♀️ Runtime

그리고 서버를 띄운 후 http://localhost:8080/swagger-ui/ 로 접속해보면 swagger-ui 화면을 볼 수 있다.

주의할 것은 swagger 2.0과 달리 기본 swagger-ui 접속 Url이 http://localhost:8080/swagger-ui.html이 아닌 http://localhost:8080/swagger-ui/로 바뀌었다는 점이다. 이 점을 몰라서 “왜 작동이 안되지? “하면서 한참 헤맸었다.

이직한 곳에서의 주 백엔드 개발 언어가 node.js 에 nest.js를 사용하는 환경이라 좀 생소하다. 나에게 맞지 않는 옷을 입은 느낌이 자꾸 든다. 다양한 경험을 위해 이직한 만큼 이곳에서 또 충분히 성장한 후 언젠가 다시 java로 돌아갈 때가 올거라고 믿기에 자바를 계속 갈고 닦기 위해 개인 프로젝트를 자바로 진행하려 한다.
화이팅~

👻 References

Comment and share

원래 Enum에 관련된 글도 이 전 블로그에서 작성했던 적이 있었다. 한번 글들을 날려먹고 두번째 작성하는 글이므로,
이 전에 작성했던 글만큼의 정성을 들일 수 있을까…..?

일단 Java Enum에 대해서는 내가 지금부터 쓰는 글보다 잘 써 놓은 글이 있으니 먼저 읽어 보는것도 좋을 것 같다.

enum을 사용하는 이유는 여러 가지가 있으나, 가장 큰 이유는 DB의 코드값과 달리 IDE 또는 컴파일 단계에서 지원을 받을수 있다는게 가장 큰 이유일 것이다. 자동완성도 지원하며 매직넘버와 달리 오타를 냈다면 컴파일 단계에서 잡아준다. 그말이 그말이잖..

장점, 장점을 보자

컴파일 단계에서 지원을 받을 수 있다

IDE에서는 자동완성도 되고, 오타가 났을 경우에 런타임 에러가 아닌 컴파일 에러를 내므로 버그를 만들 확률이 줄어든다.(어디까지나 완벽히 없어지는건 아니다. 버그 장인은 도구를 탓하지 않는다 )

리팩토링 또는 수정이 쉽다

값이 추가 되더라도 enum값 하나만 추가 하면 된다. 혹시 enum이 내포하고 있는 값이 달라진다면 enum만 수정하면 된다.

그 외에도

기본적으로 enum은 선언시 불변객체로 생성되므로 여러번 할당하더라도 새로 object를 생성하는 비용 부담이 덜하고, String과는 달리 equals를 사용하여 비교할 필요가 없다. == 를 사용하여 비교해도 잘 작동한다.

그렇다면 단점은?

배포 문제에서 자유롭지 못하다

컴파일 단계에서 작동해서 IDE의 지원을 받을 수 있다는 건, 반대로 배포 문제에서 자유롭지 못하다는 뜻이기도 하다. 간단한 값 추가가 있다고 하더라도 DB에서 사용하는 코드와는 달리 배포를 해야 한다. 요즘은 CI/CD가 워낙 잘 돼있는데다가 무중단 배포 환경도 손쉽게 구축하여 사용하는 추세라 크게 문제가 안된다고 할 수 있지만, 그렇다고 하더라도 분명 DB에서 손쉽게 추가하는 것에 비하면 부담이 되는 부분은 사실이다.

만약 DB에서 일원화된 persistant 영역에서 관리하는 코드값이라고 하면 코드만 추가 해주면 끝이지만, 여러 서비스에서 사용하는 값이라면 각 서비스 별로 챙겨서 배포해야 하며, 그러다 보면 누락하는 서비스가 있을수도 있다. (그게 우리 서비스도 이랬던 적이 있더랬..) 또한 이런식으로 누락하는 서비스에서 발생하는 오류는 단순히 DB 코드를 추가했다가 생기는 오류보다 더 크리티컬 하다.

enum이 적극적으로 쓰이지 않던 때에는 거의 모든 상태값들을 코드 테이블을 하나 만들어놓고 다 때려 박았다. 사실 배포 문제에서 자유롭지 못한 문제가 있지만, 생각보다 우리가 코드라고 생각하는 아이들은 사실은 코드가 아닌 경우가 많다. 자주 바뀌는 값과 바뀌지 않는 값을 잘 구분해서 자주 바뀌지 않는 녀석들을 enum으로 선언해서 사용하자.

숫자만 넣어서 만들수는 없다

망치를 든 사람에게는 모든게 못으로 보인다고, enum의 맛을 알게 되면 모든 코드를 enum으로 바꾸고 싶은 욕망에 사로잡히게 된다. 그러나 이렇게 enum으로 하나둘씩 바꾸다 보면 숫자로만 돼있는 코드들은 enum으로 만들 수 없다는 것을 깨닿게 된다.
enum의 상태값들은 java의 필드명 정의하는 규칙과 같이 숫자로만 구성할 수는 없다.

사실 대부분 enum 상태값으로 정의하고자 하는 속성은 숫자가 아닌 다른 뜻을 갖고 있는 경우가 많이 있지만 진짜 숫자만 있는 경우라면? 대체할 만한 단어가 없다면??? 그럼 그냥 다른 문자를 붙이는 수밖에 없다.

1
2
3
enum Numbers {
_1, _2, _3;
}

별로 맘에는 안든다. 그렇지만 범위를 제한할 수 없는 int 필드보다는 낫다.

1
2
3
enum Numbers {
1ST, 2ND, 3RD;
}

뭐 이런것도 나쁘지는 않다.

좀 더 고급지게 사용해보자

자바의 enum은 다른 언어의 enum에 비해서 좀 더 활용도가 높다. 사실 java에서 enum은 하나의 완전한 클래스로 취급 되는데 이 때문에 enum도 인스터스 변수를 가질수도 있고, interface를 implements 하도록 하거나 abstract로 선언할 수도 있으며 method나 static method를 만들수도 있다.

아래 예제를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum Sequences{
FIRST(1),
SECOND(2),
THIRD(3);

private int order;

Sequences(int order) {
this.order = order;
}

public int getOrder() {
return order;
}
}

이렇게 단순하게 사용하는 경우는 별로 없지만(심지어 java는 ordinal() 이라는 기본 메소드를 제공한다. 그러나 ordinal은 가급적 사용하지 않는것이 좋다. 이유는 후술) 코드에서 보이는 바와 같이 클래스의 모습과 크게 다르지 않다. 더욱이 자바의 enum은 이런 변수를 하나만 사용 가능한 것이 아니라 여러개가 사용 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
enum Sequences{
FIRST(1, "1st"),
SECOND(2, "2nd"),
THIRD(3, "3rd");

private int order;
private String shorten;

Sequences(int order, String shorten) {
this.order = order;
this.shorten = shorten;
}

public int getOrder() {
return order;
}

public String getShorten() {
return shorten;
}
}

참고로 FIRST(1, "1st") 이렇게 enum을 선언하는 구문은 생성자 메소드를 호출하는 방식이기 때문에 생성자를 어떻게 만드느냐에 따라 다른 방식으로도 사용 가능하다. 또한 enum은 static 영역에 저장되기 때문에 enum을 호출할때마다 생성자를 호출하는것이 아니어서 알고리즘의 효율성을 그렇게까지 신경쓰지 않아도 된다.

내가 즐겨 쓰는 방법이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
enum Media {
MOVIE("horror, comedy, sf, family"),
MUSIC("dance, balad, rock, classic");

private Set<String> genreSet;

Media(String genreString) {
this.genreSet = Arrays.asList(genreString.split(","))
.stream()
.map(String::trim)
.collect(Collectors.toSet());
}

public boolean contains(String genre) {
return this.genreSet.contains(genre);
}

public static Media determine(String genre) {
return Arrays.asList(Media.values()).stream()
.filter(media -> media.contains(genre))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("there is no matched genre"));
}

}

물론 아래 코드처럼 생성자에서 array를 바로 만들어서 받을수도 있겠으나 내 취향은 위 코드가 더 맞아서 주로 이렇게 쓴다. (무엇보다 set의 contains 시간 복잡도가 list보다 더 유리하다)

1
2
3
4
5
6
7
8
9
10
11
enum Media {

MOVIE(Arrays.asList("horror", "comedy", "sf", "family")),
MUSIC(Arrays.asList("dance", "comedy", "sf", "family"));

private List<String> genreSet;

Media(List<String> genreSet) {
this.genreSet = genreSet;
}
...

또한 enum에서 변수로 익명함수를 담고 있을 수도 있기 때문에 이를 사용하면 더욱더 활용도가 높아진다. 간단한 사칙연산을 수행하는 enum이다.
(Lombok도 잘 먹혀서, 아래부터는 lombok을 활용해 constructor를 만들어 보겠다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@AllArgsConstructor
enum Calc {
ADD("+", (a, b) -> a + b),
SUBTRACT("-", (a, b) -> a - b),
TIMES("*", (a, b) -> a * b),
MOD("/", (a, b) -> a / b);

@Getter
private String character;

private BiFunction<Integer, Integer, Integer> calculation;

public int calc(int a, int b) {
return calculation.apply(a, b);
}
}

아래는 위와 같은 일을 하는 코드를 abstract 메소드를 활용하여 만들어 본 코드이다. (java7)
이렇게도 사용 가능하니 적절히 필요에 따라 원하는 방법대로 사용하면 좋다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Getter
@AllArgsConstructor
public enum Calc {
ADD("+"){
@Override
public int calc(int a, int b){
return a + b;
}
},
SUBTRACT("−"){
@Override
public int calc(int a, int b){
return a - b;
}
},
TIMES("×"){
@Override
public int calc(int a, int b) {
return a * b;
}
},
MOD("÷"){
@Override
public int calc(int a, int b){
return a / b;
}
};

private String character;

public abstract int calc(int a, int b);
}

이런식으로 enum에 디자인 패턴을 얹어 사용하면 코드들이 단순 명료해 지는것을 느낄 수 있을 것이다.

ordinal()을 사용하지 않아야 하나?

아까 잠깐 언급하고 넘어간 ordinal() 메소드에 대해 이야기 해보자. enum은 순서를 가진 열거형이고, 그 순서값을 int로 반환하는 ordinal()이라는 메소드를 제공해준다. 그러나 ordinal을 사용하게 되면 나중에 유지보수에 문제가 발생하게 된다. (이 부분은 effective java item 35를 보면 자세히 나온다)

중간에 새로운 enum 요소가 끼어 드는 경우 새로운 요소 뒤에 위치한 enum들의 연산은 모두 망가지게 된다.

따라서 ordinal을 사용하는 것보다 차라리 위에서 만든 Sequence enum처럼 인스턴스 필드를 사용하여 int value를 담아두고 이를 return하는 getter를 사용하는것이 더 좋다.


내가 java 에서 enum을 사용하는 방법은 이정도이다. 물론 더 다양 하게 사용하는 방법도 있다. (enum 내부에 또 enum을 정의해 사용한다던지, 좀 다른 이야기지만 EnumMap을 사용하는 방법도 있고) 그러나 이정도만 알아도 java enum 초보 티는 좀 벗었다고 할 수 있지 않을까?

끗.

Comment and share

사실 이 이야기는 Effective Java 6장 - 불필요한 객체 생성을 피하라 에 나오는 아주 기초적인 이야기 이다. 그러나 습관이 되지 않아 생각없이 개발하다보면 어느새 이 규칙을 어겨서 어김없이 리팩토링 대상이 되곤 하는데다가, 막상 개발하면서 쓰려다 보면 사용 방법이 잘 생각나지 않아 메모할 겸 짧은 글을 작성 해본다.
(요즘 좀 정신이 나가 있어서 공부는 꾸준히 하기는 한다만, 공부하는 양은 좀 줄었고, 진도는 좀처럼 나가지가 않는다. 에라이)

일반적으로 Java에서 흔히 사용할 수 있는 정규 표현식의 예를 보자. (Junit5)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Utils {
public static boolean hasOnlyNumbers(String s) {
return s.matches("[\\d]*");
}
}

@DisplayName("Utils 테스트")
class UtilsTest {
@Test
@DisplayName("숫자 외의 문자가 있을 경우 false를 return 해야 한다")
void should_return_false_when_has_other_characters() {
String numbersWithCharacter = "1a2s3d4f5g6";
boolean expected = false;

boolean result = Utils.hasOnlyNumbers(numbersWithCharacter);

assertEquals(expected, result);
}
@Test
@DisplayName("숫자외의 문자가 없을 경우 true를 return 해야 한다")
void should_return_true_when_has_only_numbers() {
String numbersWithCharacter = "123456789";
boolean expected = true;

boolean result = Utils.hasOnlyNumbers(numbersWithCharacter);

assertEquals(expected, result);
}
}

오케이, 원하는 바를 손쉽게 얻었다. 그러나 지금 얻은 결과는 성능상 좋지 못하다. 사실 일반적인 상황에서 String의 matches 메소드를 사용하는게 엄청나게 성능에 문제를 주는 경우는 별로 없으나, 습관이 중요한데다가, 실제로 이런 코드들이 쌓여 문제를 발생하는 경우도 있으니 좋은 습관을 들이는 게 좋다.
이펙티브 자바는 String.matches를 사용하는 것보다 더 고급지고어렵고 성능이 좋은 방법을 알고 있다.

Pattern 클래스를 사용해 보자

바로 Pattern클래스를 사용해 같은 일을 더 성능 좋게 사용할 수 있다.
사실 위 예제의 코드는 메소드가 실행 될때마다 내부적으로 Pattern 인스턴스를 만들고 사용 후 버리는 작업을 수행하게 되는데, 이 Pattern을 final static 객체로 미리 선언해두고 사용하게 되면 메소드가 호출될 때마다 새로 생성하고 버리는 작업이 없어지게 된다.
아래 코드를 보자

1
2
3
4
5
6
public class Utils {
private static final Pattern ONLY_NUMBERS_PATTERN = Pattern.compile("[\\d]*");
public static boolean hasOnlyNumbers(String s) {
return ONLY_NUMBERS_PATTERN.matcher(s).matches();
}
}

테스트를 잘 통과하는 것을 볼 수 있다. 자, 그럼 얼마만큼의 성능 차이가 날까?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@DisplayName("Utils 테스트")
class UtilsTest {

final int times = 10000000;

@Test
@DisplayName("리팩토링 후")
void test_refactor() {
final String numbersWithCharacter = "1a2s3d4f5g6";
for(int i=0; i < times; i++){
Utils.hasOnlyNumbersRefactor(numbersWithCharacter);
}

}
@Test
@DisplayName("리팩토링 전")
void test_before_refactor() {
final String numbersWithCharacter = "1a2s3d4f5g6";
for(int i=0; i < times; i++){
Utils.hasOnlyNumbersOriginal(numbersWithCharacter);
}
}
}

컴퓨팅 성능마다, JVM 셋팅마다 다르겠지만, 메소드 수행을 1000만회 진행 했을때 꽤나 차이가 나는 것을 눈으로 볼 수 있었다.
(2.35초 -> 0.83초)

이펙티브 자바의 아이템6 에는 이 외에도 다른 팁들도 있으나 일단 오늘은 정규 표현식 부분만 정리했다.

정규 표현식을 사용할때는 기억하자.

  1. static final Pattern 으로 불변객체를 생성해서 사용하자
  2. Pattern p = Pattern.compile(“정규식”);
  3. p.matcher(string).matches();

Comment and share

Spring boot 에서는 application.yml (또는 application.properties) 파일의 내용을 빈의 변수에 바인딩하도록 도와주는 어노테이션이 있다.
@Value("${property.value}") 뭐 이런식으로 사용하는데 변수가 늘어남에 따라 어노테이션이 많아져서 가독성을 해치는 경우가 있고, 일일히 지정해주기가 귀찮을 경우 쓸 수 있는 방법인 @ConfigurationProperties 어노테이션에 대해서 간단히 적어 보려 한다.

사용방법

@ConfigurationProperties 는 클래스에 선언하는 어노테이션이다.
간단히 아래 예시와 같이 사용한다.

먼저 gradle(또는 maven 빌드 툴)에 아래 dependency를 설정한다.

1
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'

yml에 필요한 값들을 설정하고

1
2
3
4
user:
conf:
property1: "value1"
property2: "value2"

값들을 사용하고자 하는 클래스에 @ConfigurationProperties 어노테이션을 추가한다. 여기서 prefix도 설정할 수 있다. (user.conf는 예시를 위해 작성해 놓은 것이고, prefix는 다른 값으로도 사용할 수 있다.)
주의할 점은 @Value 어노테이션을 활용해 값을 바인딩 하는것과 다르게, 반드시 setter 메소드를 필요로 한다.
@ConfigurationProperties는 setter 메소드를 사용해 설정값을 바인딩 하기 때문이다. 여기서는 클래스에 @Setter 롬복 어노테이션을 사용해 한번에 정의했다.

1
2
3
4
5
6
@Setter
@ConfigurationProperties(prefix = "user.conf")
public class SomeClass {
private String property1;
private String property2;
}

Comment and share

IntelliJ에서 .http로 테스트 하기

나는 java 개발을 할 때 IntelliJ를 사용한다. 지금 다니고 있는 회사에 입사하기 전까지는 eclipse 만 사용해봤었고,
IntelliJ는 사실 존재 자체도 모르고 있었다. 그러나 지금은 intelliJ 없이는 java개발을 할 수 없는 몸이 되어버렷!! 다..
IntelliJ는 여러 편의 기능을 번들로 제공하고 있는데 그 중 하나가 지금 이야기 하고자 하는 http client 툴이다.

사실 IntelliJ의 Http 툴의 사용방법을 설명한 글을 이전에 블로그에 한번 작성한 적이 있었으나 안타깝게도 블로그와 함께 날려먹었고, 이 툴에 관해 잘 설명해 놓은 글은 굳이 내가 정리하지 않더라도 많이 있다. 그럼에도 불구하고 또 이 툴에 대해서 정리하는 이유는 내가 잘 보려고

회사에서 개발 일을 하면서 많은 지식을 익히고 이 지식들로 코딩을 하지만, 돌아서고 나면 잊어버린다. 물론 코드에 작업 내용이 남아있어 다시 열어본다면 이해하고 동일한 코드를 만들 수 있지만, 사실 이 코드의 소유는 내가 아니라 회사다. 퇴사를 한다면?? 내가 동일한 코드를 만들 수 있을까? 그래서 내 것으로 만들기 위해 하나씩 정리 하려고 한다.

.http 소개

이 툴의 명칭은 HTTP Client 쯤 되는것 같다. (HTTP client in IntelliJ IDEA code editor)
안타깝게도 ultimate 딱지가 붙어 있는걸 봐서는 intelliJ 커뮤니티 에디션에서는 사용하지 못하는 듯 하다. 레퍼런스 페이지는 #여기

간단히 말해 http 호출을 해볼 수 있는 툴이다.

.http 를 쓰는 이유

익히지 않고 직관적으로 쓰기에는 물론 postman이 더 낫다. swagger도 많이 쓴다. 설치하기 귀찮으면 curl을 사용하기도 한다.
내가 .http 를 쓰는 가장 큰 이유는

  1. Git을 통한 형상 관리
  2. Git으로 팀원과 쉽게 공유가 가능
  3. 테스트 툴이므로 초록 막대를 보며 마음의 안정을 얻을 수 있다. (또는 빨간 막대를 볼수도..)

뭐 이 외에도 쓰다보면 여러 장점들을 느낄 수 있겠지만, 일단 이 세가지만으로도 충분히 좋다.

기본 사용법

  1. intelliJ의 프로젝트 explorer에서 적당한 곳에 마우스 우클릭 > NEW > HTTP Request

  1. 적당한 파일명을 입력 후 엔터를 치면 .http 로 끝나는 파일이 생성 되고 에디터로 그 파일을 연다.

이 때 주의할 점 파일명을 status로는 하지 말자. 왠지 모르겠지만, status.http 파일에서는 http 테스트가 활성화가 되지 않는다.

  1. 이제부터 테스트를 만들면 된다. 아직 우리는 사용법을 잘 모르므로 Examples를 눌러보자. 그럼 친절하게 아래와 같은 내용의 파일이 열린다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
### GET request with a header
GET https://httpbin.org/ip
Accept: application/json

### GET request with parameter
GET https://httpbin.org/get?show_env=1
Accept: application/json

### GET request with environment variables
GET {{host}}/get?show_env={{show_env}}
Accept: application/json

### GET request with disabled redirects
# @no-redirect
GET http://httpbin.org/status/301

### GET request with dynamic variables
GET http://httpbin.org/anything?id={{$uuid}}&ts={{$timestamp}}

###

  1. 각 테스트는 ###로 구분한다.
  2. 제일 윗줄에 메소드 URL 형식으로 호출하고자 하는 URL을 정의한다.
  3. 헤더는 URL 바로 아랫줄에 키: 밸류 형태로, 붙이고자 하는 헤더가 여러개일 경우 계속 줄을 이어서 써준다.
  4. 예제에는 안 나왔지만 requestBody를 같이 보내고자 할 경우, 헤더 밑으로 두줄 바꾸고 그 밑에 입력해주면 된다.

기본 사용방법은 이정도이고, {{$uuid}}{{host}} 같은게 보이는 것 같지만 기분탓이라 생각하고 일단 넘어가자.
대략 사용방법은 봤으니, 우리가 테스트 하고자 하는걸 입력 해보자.

1
GET https://google.com

난 이렇게 입력 했다. 좀 편한 방법을 쓰고자 한다면 우측 상단 메뉴에 Add Request 를 마우스로 누르면 snippet 이 나오니 참고할 수 있다.

그리고 초록색 재생 화살표를 누르면 실행되고, 화면 아래에 결과가 나온다.

► 눌러눌러!!

구글 호출 했을때 나오는 결과를 볼 수가 있다.

환경변수 설정해서 사용하기

자세한 설명 링크

위에 예시로 제시한 테스트 중에 {{host}} 같이 이중 블릿으로 감싼 아이들이 보인다. .http 테스트에서 환경 변수들을 사용할 수 있는데, 여러 환경에 따른 값들을 미리 설정해놓고 필요할때마다 선택해서 실행 할 수 있도록 편리한 기능을 제공해 준다.

.http client는 두 종류의 변수를 사용할 수 있는데, Dynamic 변수와 직접 정의한 환경 변수이다. 다이나믹 변수는 $로 시작하고, 아래 세 종류가 있다.

  • $uuid: 우리가 알고있는 UUID
  • $timestamp: Unix Timestamp형태의 현재 시각
  • $randomInt: 0에서 1000 사이의 랜덤 숫자

그리고 우리가 알아볼 환경변수. 환경변수는 프로젝트 내에 rest-client.env.json (또는 http-client.env.json도 가능) 라는 이름의 json 파일을 생성해서 사용한다. 레퍼런스 문서에서 제공하는 예제를 살펴보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"development": {
"host": "localhost",
"id-value": 12345,
"username": "",
"password": "",
"my-var": "my-dev-value"
},

"production": {
"host": "example.com",
"id-value": 6789,
"username": "",
"password": "",
"my-var": "my-prod-value"
}
}

이 예제에서는 development, production 환경을 사용할 수 있고, 그 내부에 정의 돼있는 host, id-value 와 같은 값들을 미리 정의 해놨다고 이해하면 된다. 테스트 할때 위 예제에서 봤듯이 http://{{host}}/index.html 와 같은 형식으로 사용할 수 있다. 물론 이 값들은 url 뿐만 아니라 헤더나 request body 영역에서도 치환해서 사용할 수 있다.

username과 password는 비어두었는데, 개인정보 등 공유되어서는 안될 민감한 정보들을 별도로 정의하기 위해서 비워두었다. rest-client.private.env.json (또는 http-client.private.env.json) 라는 파일 명으로 별도로 정의하여 사용할 수 있는데, 이는 형상관리에서 기본으로 제외되도록 설정돼있다고 한다.

1
2
3
4
5
6
7
8
9
10
11
{
"development": {
"username": "dev-user",
"password": "dev-password"
},

"production": {
"username": "user",
"password": "password"
}
}

rest-client.env.json 와 같이 이런 형태로 정의 해서 사용할 수 있다.

테스트 결과값 받아서 사용하기

이렇게 환경변수를 설정하는 방법은 rest-client.env.json 파일을 통해 미리 정의하는 방법 말고 또 한가지가 존재한다. 바로 http 호출 후 response handler를 정의해서 변수로 설정하는 방법이다. 아래 예제를 살펴보자. (참고로 response handler는 javascript 문법을 사용한다. 또한 자동완성까지 지원한다. ㄴㅇㄱ)

1
2
3
4
5
6
7
8
9
POST http://localhost/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=read_profile

> {% //response handler
client.global.set("access_token", response.body.access_token);
client.log(client.global.get("access_token"));
%}

POST로 http://localhost/oauth/token를 호출하고 받는 response에서 response.body 의 access_token 값을 받아 client.global.set()이라는 함수로 변수 설정이 가능한데, 이 스크립트의 경우 “access_token”이라는 변수에 response.body 객체의 access_token 값을 할당 했다. (response.body 가 json형태일 경우다)

response 객체는 body 말고도 다른 속성들도 제공 해준다. 에디터에서 response.을 찍어서 우리의 친절한 intelliJ 자동완성의 도움을 받아 살펴보자.

body 외에 contentType, headers, status 값들을 제공한다.

이를 잘 활용하면 ouath 토큰을 발급받아서 환경변수에 넣는 스크립트를 만들고, 그 이후에 실행하는 스크립트에서 발급받은 토큰을 포함하여 request 테스트를 하는 등의 케이스에서 활용할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
### Get Token
POST http://localhost/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=read_profile

> {% //response handler
client.global.set("access_token", response.body.access_token);
client.log(client.global.get("access_token"));
%}

###

GET http://localhost/data
Authorization: bearer {{access_token}}

response handler는 이렇게 테스트 스크립트 내에 inline으로 작성하지만 별도의 js 파일로 만들어서 include 하는 방법도 존재한다.

1
2
3
GET host/api/test

> scripts/my-script.js

초록 막대. 초록 막대를 보자!!

거의 다 와 간다. 글 머리에 초록 막대를 볼 수 있다고 적어 놓았는데, 이제 초록 막대를 보는 방법, 즉 테스트 케이스 답게 스크립트로 만드는 방법을 알아보자.

간단하게는 아래와 같은 방법으로 사용할 수 있다.

1
2
3
4
5
6
7
GET https://httpbin.org/status/200

> {%
client.test("Request executed successfully", function() {
client.assert(response.status === 200, "Response status is not 200");
});
%}

client.assert만으로도 잘 작동하기는 하나, 제대로 된 테스트 케이스는 테스트 정의가 잘 돼 있어야 하므로 client.test로 테스트 케이스가 작동하는 상황에 대해서 설명한 후 두번째 파라미터 함수로 실제 수행되는 테스트 코드를 넣어주면 된다.

client.assert는 첫번째 파라미터에 테스트 하고자 하는 수식, 두번째 파라미터로 테스트가 실패할 경우 표시될 메세지를 넣어준다.

이제 ►를 눌러 실행해보자.

이제 마음의 평안을 얻을 수 있다. response.status 뿐만 아니라 body에서 어떤 결과를 받아왔는지 등으로도 테스트 케이스를 만들 수 있으니 잘 활용하면 좋다.

intelliJ에서 http로 테스트하기 끗!!

Comment and share

사족

그간 소중히(?) 작성하며(자주는 아니지만) 차곡차곡 써왔던 개인 블로그 글을 어느순간 날리게 되었다. 왜 날아가게 되었는지는 아직도 모른다. 사실 찐 원인은 간단하다.

“넌 평소에 보안과 백업을 소중히 하지 않았지”

1
2
3
1. 사실 블로그는 홈 서버에 wordpress 로 대충 글만 올려놓고 있었는데, 
"누가 내 블로그에 관심이나 있겠어?" 라는 마인드로 보안쪽으로는 전혀 신경쓰지도, 관리하지도 않음.
2. 그와중에 백업도주기적으로 안함

사실 상용 서비스를 운용 할때는 보안과 백업은 기본중의 기본인 것을 쯧쯧.

그렇게 5개월치의 글을 날려먹고 한동안 글 쓸 의욕을 잃고 헤매다, 다시 마음을 다잡고 하나씩 글을 써 보고자 한다.

그리고 보안과 백업은 상대적으로 홈서버 운용보다 자유로는 github pages와 hexo로 옮겨왔다. 익힐게 많지만 이것도 또 하나의 즐거움 이리라.

올바른 Commit 메세지를 작성하는 방법

요즘은 progit이라는 책을 읽으며 git에 대해 공부하고 있다. “현업에서 git을 사용하고 있으므로, 이미 git은 어느정도 사용하는거 아냐?” 라고 할 수 있겠지만, 반은 맞고 반은 틀리다. 그냥 commit을 하고 pull request를 날리고, 코드 리뷰를 하고, merge를 하고 하는 수준은 할 수 있으나 “잘~” 쓰지는 못하고 있다. 그래서 git을 공부를 한다.

오늘은 그 중 가장 기초중의 상기초 커밋 메세지를 작성하는 방법에 대해서 쓰려고 한다. 깃에서 커밋 메세지를 남기는 방법은 간단하다.

1
$ git commit -m "커밋메세지"

잘 못 작성한 커밋 메세지의 예

“이 커밋 메세지를 잘 남기라구? 어떻게 더 잘 남기지??”

이런 의문이 생긴다면, 잘못 작성한 커밋 메세지의 예 부터 살펴보자

멀리 갈 것도 없다. 작년 초의 내가 남긴 커밋이다. 황량하고 알 수 없는 커밋 메세지의 처참한 모습 이루 말할 수가 없다.

물론, 이 총체적 난국의 git 흔적들은 비단 커밋 메세지만이 문제가 아니지만, 오늘의 주제는 커밋 메세지 이므로 커밋 메세지에 집중해서 살펴보자

의미없는 커밋 메세지에, 심지어는 그마저도 귀찮았는지 . 도 있다. 미쳤구나 물론 이 커밋 메세지를 남기던 나는 프로젝트 마감 일정에 쫒기어 정말 제정신이 아니었는데다가, 같이 개발하는 동료는 한명밖에 없었고, 서로 바로 옆자리에 앉아 빈번히 커뮤니케이션 하고 있었기 때문에 서로의 커밋 메세지가 외계어로 쓰이던 암호로 넣던 별 탈이 없었다. 그당시에는..

그러나, 소프트웨어는 한번 만들어 놓으면 끝이 아니다. 그리고 3개월 정도가 지나 열어 본 코드는 “내가 왜 이렇게 짰지? 이거 정말 내가 짠거 맞나?” 할정도로 낮설은 경우가 있다. (3개월 전의 나는 내가 아니라 타인이다.) 이럴 때 찾아보게 되는게 커밋 히스토린데 커밋 히스토리가 무의미한 커밋 메세지로 채워져 있따면.. 심지어 . 이라면?! 노답. 핵노답
생각만 해도 아찔한데 난 이미 겪었다. 이런 아찔한 순간을.. 캡쳐의 히스토리가 이야기 해주고 있지 않은가? 이건 경험담이다..

어떻게 좋은 커밋 메세지를 작성할 것인가?

좋은 커밋 메세지를 작성하는 방법에 대해서는 이미 여러 잘 쓰여진 글들이 있다.

그 중 하나를 소개해 본다.

git 커밋 메세지 작성법

아래는 위 글에서 발췌한 훌륭한 git 커밋 메세지의 일곱가지 규칙이다.

  1. 제목과 본문을 빈 행으로 분리한다
  2. 제목 행을 50자로 제한한다
  3. 제목 행 첫 글자는 대문자로 쓴다
  4. 제목 행 끝에 마침표를 넣지 않는다
  5. 제목 행에 명령문을 사용한다
  6. 본문을 72자 단위로 개행한다
  7. 어떻게 보다는 무엇과 왜를 설명한다

위 글에 예시도 훌륭하게 나와있다. 핵심은 최대한 함축적으로 간결하게 제목을 작성하고,
본문에 상세한 내용을 기술 하라는 것이다. 나도 위 일곱가지 규칙에 동의 한다. 그러나 몇몇가지 우리 실정에 맞지 않는 규칙들도 있다. 이 일곱가지 규칙은 영어로 커밋 메세지를 작성할 때의 규칙인데, 우리는 한글로 커밋 메세지를 작성할 때도 많기 때문이다.

2. 제목 행을 50자로 제한한다

이건 영어 기준이다. 한글 기준으로는 30자 정도가 적당한 것으로 보인다.
터미널에서 열었을때, 영어 2글자-> 한글 한글자 정도의 너비를 갖고 있기 때문이다.

3. 제목 행 첫 글자는 대문자로 쓴다

음.. 어… 음.. 영어로 쓸때만 지키면 된다

5. 제목 행에 명령문을 사용한다

사실 오늘 커밋 메세지에 대한 글을 쓰는 이유는 이 항목에 대한 이유가 크다. 일단, 블로그 글들이 그렇듯이, 이 이하 기술하는 글들은 내 개인적인 의견임을 감안하길 바란다.

이 항목 때문에 한글 커밋 메세지에 대해서도 명령문을 사용하는것을 strict하게 지켜서

“스프링 프레임워크의 버전을 업그레이드 하라”
“NullPointerException 방지 코드를 넣어라”

하는 식으로 커밋 메세지를 작성하는 경우를 꽤나 보았고, 이렇게 쓰기를 권고하는 블로그 글들도 많이 보았다. 개인적으로는 이런 형태의 커밋 메세지가 굉장히 이질적이기 때문에 별로 좋아하지 않는데, 왜 이렇게 작성 해야 하는지에 대해서도 열심히 찾아 보았는다.
개인적으로 판단한 이 컨벤션의 시초는 “영어로 커밋 메세지를 작성” 할 때의 규칙을 그대로 한글로 들고 왔기 때문에 이런 일이 발생했다고 본다.

I refactored subsystem X for readability => (X)
Refactor subsystem X for readability => (O)

쓸때없는 I는 없애버리고(글자 제한이 있으므로), git 시스템의 관점에서 수행한 일을 현재시제로 작성하게 되면, 이게 바로 영문법에서의 명령형이다.

위 링크에 소개한 글에서는 이런 식으로 작성할 것을 권고 한다.

만약 이 커밋이 적용되면 이 커밋은 가독성을 위해 서브시스템 X를 리팩토링한다
=> 가독성을 위해 서브시스템 X를 리팩토링한다
만약 이 커밋이 적용되면 이 커밋은 Getting Started 문서를 갱신한다
=> Getting Started 문서를 갱신한다

그러나 좀 부족한 것이 있다. 영문 명령형으로 커밋 메세지를 작성하게 되면 극명히 보이는 장점이 있다.

refactor subsystem X for readability
update getting started documentation
remove deprecated methods
release version 1.0.0
merge pull request #123 from user/branch

동사 + 목적어 로 표현되는 영문법 어순 특성 상, 어떤 행위를 했는지가 문장 제일 앞에 정렬 된다는 점이다. 이런 장점 때문에 더더욱 명령형으로 커밋 메세지를 작성 하라고 하는 것인데, 한글에서 그대로 이런 컨벤션을 사용하게 될 경우, 우측정렬을 하여 커밋 메세지를 보지 않는 이상 이런 장점은 누릴 수가 없게 된다.

방법 1.

따라서 한글로 작성하게 될 경우, 커밋이 담고있는 가장 큰 함축적인 행위의 동사를 제일 앞에 말머리로 붙여주고, 뒤에 커밋 메세지를 작성하는 것이 좋을 것 같다. 자연스런 문장을 위해 단어가 두번 들어가는것도 나쁘지 않다.

리팩토링 - 서브 시스템 X. 가독성 향상 목적
갱신 - Getting Started 문서

방법 2.

아니면 팀만이 사용하는 기호를 사용해 핵심 행위를 가리키는 단어를 강조하는 것도 좋은 방법 중의 하나일 것이다.

가독성을 위해 서브 시스템 X를 [리팩토링]
Getting Started 문서 [갱신]

방법 3.

세번째 방법은, 어차피 프로그램 개발에서 하는 행위들은 몇가지로 정해져 있고, 이는 카테고리화가 가능할 것 같다. Add, Refactor, Remove, Release 등등.. 이런 카테고리들을 팀별로 미리 정의 해 두고 앞부분에 prefix를 붙이는 것도 한 방법이다.

refactor - 가독성을 위해 서브 시스템 X를
update - Getting Started 문서

마무으리

뭐, 이 외에도 여러 아이디어가 있을 수 있겠다. 핵심은, 함축적인 커밋 메세지를 한눈에 파악할 수 있도록 가독성을 확보하는 자신만의, 팀만의 방법을 찾는 것이다.
(본인도 아직 팀원들과 이야기 하며 더 연구하고 찾는 중이다.) 그러나 이런 방법들이 고민스러워 컨벤션을 못 정하겠다면 일단 . 만은 피하자.

순간 바쁘고 귀찮다고 .만을 남겼을때 지금의 나에게 화내고 있을 3개월 후의 나를 위해서, 1분만 더 고민하고 커밋 메세지를 남기는 습관을 들이는게 제일 중요하다.

그럼 난 이만, 1년 전의 나에게 벌을 주러 가보려 한다..

Comment and share

  • page 1 of 1

shockshot@naver.com

author.bio


author.job