본문으로 건너뛰기

· 약 1분
Green

gitlab 에서 CI/CD 를 진행 할 수 있는 방법을 정리 해봐요.

cast

  • gitlab server 소스 코드 저장소를 가지고 있는 서버
  • repository 소스코드 저장소
  • deploy server 배포 대상 서버
  • gitlab-runner 이 모든 일을 가능케 해주는 주인공

synopsys

  1. deploy-servergitlab-runner 를 설치하고,
  2. gitlab-runner register 명령으로 gitlab-server를 연결하고,
  3. repository.gitlab-ci.yml를 작성

scenario

wip...

· 약 1분
Green

두번이나 진행했는데... 계속 까먹는다. 어렵게 다시 메모를 해놓기로 마음 먹었음.

openssl rsa -in source.key -out target.key

Enter pass phrase for source.key: ◼︎

openssl 명령어 후에, 비밀번호를 입력하면, 끝!

· 약 4분
Green

거의 3년만에 리액트를 다시 해보네요. 낯설어...

Installation

느낌적인 느낌으로, 뭔가 App.js 도 엄청 간단해진 것 같고 여러모로 발전한 것 같네요.

npx create-react-app test

test 프로젝트를 하나 만들고,

pnpm install

설치해요.

PNPM

pnpm 은 새로운 패키지 매니저인데, 중복 모듈들을 심볼릭 링크로 처리하는 등등의 기법으로 무진장 빠르다네요.
실제 체감속도가 만족스럽긴해요.

eslint

npx eslint --init 

초기화 하면,

You can also run this command directly using 'npm init @eslint/config'.
Need to install the following packages:
@eslint/create-config
Ok to proceed? __(y)__
✔ How would you like to use ESLint? · problems
✔ What type of modules does your project use? · __esm__
✔ Which framework does your project use? · __react__
✔ Does your project use TypeScript? · __No__ / Yes
✔ Where does your code run? · __browser__
✔ What format do you want your config file to be in? · __JavaScript__
Local ESLint installation not found.
The config that you've selected requires the following dependencies:

eslint-plugin-react@latest eslint@latest
✔ Would you like to install them now? · No / __Yes__
✔ Which package manager do you want to use? · __pnpm__

기본 물음에 적절히 대답을 선택해주면 되고,
그러고 나면 .eslintrc.js 이 자동 생성 되는군요.

prettier

pnpm add -D prettier eslint-config-prettier eslint-plugin-prettier

프리티어 관련 의존성을 devDependencies 로 추가하면 되겠네요.

.eslintrc.js

프리티어 설정을 추가해서 eslint 와 잘 어우러지게 하는게

.eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:prettier/recommended', // 추가
],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['react'],
rules: {
'prettier/prettier': 'error', // 추가
},
}

추가 주석된 부분을 새롭게 넣어주는 거에요. eslint 플러그인으로 등록하고, rules 항목은 프리티어 규칙 위반하면 error 가 던져지니까 컴파일이 안돼서 진행이 안되겠네요.

.prettierrc.js

프리티어 옵션은 .prettierrc.js 파일로 진행하면 돼요.
package.json 이 있는 루트 디렉토리에 파일을 만들어서,

.prettierrc.js
module.exports = {
arrowParens: 'avoid',
semi: false,
singleQuote: true,
trailingComma: 'none'
}

변경하는 설정들을 넣어줘요.

Prettier Options

모든 옵션에 대한 설명은 https://prettier.io/docs/en/options.html 에서 확인하세요.

이렇게 한 후에 IDE 에서 프리티어 실행하는 방법도 있지만

prettier .

CLI 에서 실행하면, 모든 파일들이 한방에 프리티어 설정으로 포맷팅이 되네요.

.prettierignore

git 의 tracking 을 회피하는 것과 똑같이 .prettierignore 가 있어서 프리티어를 회피할 수 있어요.

· 약 1분
Green

뭔가 다시 열심히 시작해 봅니당!

망함

이전 포스팅도 별건 없었지만... 깃헙 SSH 문제로 이리저리 돌리다가 날려먹었네요.
그저 웃지요~ 🤤

reboot

· 약 4분
Green

개요

실무에서 SPA로 구현할 수 없는 이상한(?) 구조의 웹어플리케이션을 개발하는게 다반사가 되었어요. 고객사는 한번 채택한 그 복잡하고도 어지러운 UI를 맘에 들어한 후로 계속 그런식을 고수하고 있거든요.

그래서 리엑트를 사용해서 개발하던 어떤 컴포넌트를 조금 더 범용적으로(vanilla js) 사용해보려고 시도 하면서 DOM을 조작하는 비효율적인 Javascript WebAPI에서 탈피하고 JSX를 리엑트 없이 사용해보려고 합니다.

설정

우선 tsconfig.json 에서,

{
"compilerOptions": {
"module": "es6",
"jsx": "react",
"jsxFactory": "JSX",
...

jsFactoryJSX 라고 했으니, 그 이름으로 전역 함수를 만들어줘야 해요.

export default function JSX(
element: any,
props: { [id: string]: any },
...children: any[]
): any {
console.log(arguments) // 호출시점, 인자 확인용

if (typeof element === 'function')
return element()
else return {
name: element,
props,
children
}
}

이렇게 해서 console.log 등으로 함수의 인자, 호출시점을 확인해 보면 JSX의 DOM이 파싱되는 과정을 확인 할 수 있습니다.

구현

리엑트 구현 코드를 실제로 보시면 더 자연스레 이해가 되실거에요.
제가 사용한 코드는 다음과 같아요.

JSX.ts 파일

export default function JSX(
element: any,
props: { [id: string]: any },
...children: any[]
): any {
if (typeof element === 'function')
return element()
else return {
name: element,
props,
children
}
}

export function dom(element: {
name: string,
props: { [id: string]: any },
children: any[]
},
parent: Node): void {

const tag: HTMLElement = document.createElement(element.name)
attributes(tag, element.props)

if (element.children && element.children.length) {
element.children.forEach(child => {
if (typeof child !== 'object' || !child.hasOwnProperty('name'))
tag.appendChild(document.createTextNode(child.toString()))
else {
child.props.__child = true
dom(child, tag)
}
})
}

parent.appendChild(tag)
}

index.tsx 파일

import JSX, {dom} from "./JSX"

dom(
<div>
<h1>Hi, guys!</h1>
<blockquote>
Nice to see you!
<strong>I tidy up my article.</strong>
but, you will be boring.
</blockquote>
<p>Bye guys.</p>
</div>
,
document.querySelector('#app')
)

마무리

이게 통상적인 JSX 사용법인지는 잘 모르겠어요. 여름휴가를 길게 다녀왔는데, 그전에 검색하고 방법을 찾아본걸 이제야 정리하다보니 실행은 잘 되는데 확신은 가지 않는군요.

이렇게 기록을 남긴다는게 부끄럽기도 하고, 동시에 이런 방점없는 어정정한 정보가 여러사람에게 오해를 만드는건 아닐까하는 딜레마로 포스팅을 하는게 항상 두려워요.
그러면서도 혹시 누군가, 어둠속을 헤매는 막막한 이에게 미약하나마 작은 등불이라도 되었으면하는 바람으로 용기를 내어요.

· 약 2분
Green

개요

토이프로젝트를 진행하던 중, 마우스 포인터의 좌표와 대상 요소의 위치를 비교하는 계산이 필요한 지점이 생겼어요.

jQuery.position() 이 생각나서 jQuery 를 의존성 주입해야하나 고민하다가(요즘은 jQuery를 의식적으로 거부하고 있어서)
새로운 API를 알게 되었어요.

활용

Element.getBoundingClientRect() 이 녀석 입니다. 실무에서는 고객대응 때문에 아직도 IE10 을 고려해야하는데, 이 API는 IE9 부터 사용가능하다고 하네요. (그토록 오랫동안 모르고 있었다니...)

실무에서 사용하는 IE10 에서 사용해 보았습니다.

마무리

jQuery는 쓰기 싫고 직접 요소의 부모를 찾아 html 까지 올라가면서 padding, margin, position 을 조사하면서 위치를 계산해야 하는건가 싶었는데, 좋은 방법을 찾았습니다.

한편으로는 이 유용한 것을 지금까지 왜 모르고 있었나 싶어서 부족한 나를 자책해 봅니다. (어쨌든 그래도 Love myself :)

· 약 1분
Green

typescript로 간단한 React 모듈을 개발하다가 화면 구성이 필요해서 Sass를 사용하려다가 알게 됐어요.

CRAcreate-react-app으로 구성했더니 아주 간편하게 처리가 되더라고요.

npm install node-sass

이렇게 그냥 의존관계만 만들어주면 알아서 sass-loader가 추가되나봐요.

CRA v2 부터의 변화중에 하나인 것 같은데, velopert님의 포스트에서 확인했고, 새로운 v2의 기능들을 언제나 그렇듯 잘 설명해 주셨으니 확인해 보세요.

· 약 5분
Green

벤치마킹 테스트를 해보게도 되네요.
폭주하는 서비스를 개발하는것도 아닌데, 그냥 심심풀이로 서핑하다가 보여서 해봤어요.

특징

  • Oracle 에서 JVM 개발하는 분들이 만드셨다고 하고
  • Java9 부터는 공식적으로 JDK에 같이 배포된다고 줴이줴이뤱에 훌륭한 분이 정리한 내용을 참조하면 더 많은 특징을 얻을 수 있겠어요.

시작

당연히 JMH 공식사이트에서 확인하시는게 좋겠죠. 저는 잘 모르면서 막해본거라서...
샘플 코드들이 보고 따라하기 좋은 것 같고요.

준비

프로젝트에 JMH에 관련된 dependency를 추가 해요. 저는 gradle 환경이니까,

...

dependencies {
...
testImplementation 'org.openjdk.jmh:jmh-core:1.21'
testImplementation 'org.openjdk.jmh:jmh-generator-annprocess:1.21'
}

이렇게 org.openjdk.jmhjmh-corejmh-generator-annprocess 를 추가하고, IntellJ에 플러그인 을 설치 했어요. 플러그인 없이

import org.openjdk.jmh.annotations.*;

class BenchmarkTest {

...

public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(BenchmarkTest.class.getSimpleName())
.warmupIterations(3)
.measurementIterations(1)
.forks(1)
.build();

new Runner(opt).run();

}
}

이렇게 실행해도 되고요.

실행

최근에 정적분석을 하면서 java.util.Randomjava.security.SecureRandom 으로 바꾸라고 지적된게 있었거든요. 그래서 그 두가지를 벤치마킹 해봤어요.

@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(1)
@Warmup(iterations = 1)
@BenchmarkMode(Mode.Throughput)
@Measurement(iterations = 1)
public class BenchmarkTest {

private final Random random = new Random();

private final SecureRandom secureRandom = new SecureRandom();

@Benchmark
public void measureRandom() {
random.nextDouble();
}

@Benchmark
public void measureSecureRandom() {
secureRandom.nextDouble();
}
}

실행 결과 다음과 같이 리포트 되네요.

# JMH version: 1.21
# VM version: JDK 1.8.0_181, Java HotSpot(TM) 64-Bit Server VM, 25.181-b13
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/bin/java
# VM options: -Dfile.encoding=UTF-8
# Warmup: 1 iterations, 10 s each
# Measurement: 1 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: BenchmarkTest.measureRandom

# Run progress: 0.00% complete, ETA 00:00:40
# Fork: 1 of 1
# Warmup Iteration 1: 41945.262 ops/ms
Iteration 1: 42456.015 ops/ms


Result "BenchmarkTest.measureRandom":
42456.015 ops/ms


# JMH version: 1.21
# VM version: JDK 1.8.0_181, Java HotSpot(TM) 64-Bit Server VM, 25.181-b13
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/bin/java
# VM options: -Dfile.encoding=UTF-8
# Warmup: 1 iterations, 10 s each
# Measurement: 1 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: BenchmarkTest.measureSecureRandom

# Run progress: 50.00% complete, ETA 00:00:20
# Fork: 1 of 1
# Warmup Iteration 1: 1826.455 ops/ms
Iteration 1: 1837.092 ops/ms


Result "BenchmarkTest.measureSecureRandom":
1837.092 ops/ms


# Run complete. Total time: 00:00:41

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark Mode Cnt Score Error Units
BenchmarkTest.measureRandom thrpt 42456.015 ops/ms
BenchmarkTest.measureSecureRandom thrpt 1837.092 ops/ms

Score 가 42배쯤 차이나니까 그게 성능차이라고 보면 될것 같네요. REMEMBER 부분에 적힌 글을 보니까 그냥 데이터로만 참조하라고 첨언하는 것 같아요. 재사용하는 통찰을 얻으려면 숫자들이 가리키는 걸 따라가라고 하면서요.

맺음

저는 그냥 재미로 '이런것도 있구나' 하고선 시작해봤는데 재사용성을 위해 refactoring을 하면서 채점식으로 수치를 확인해가면, 의욕도 생기고 보람도 생길지는 않을까 싶기도 해요.

더 활용도가 높을텐데 제 수준에서는 이정도 밖에 모르겠네요. 간간히 성능이슈에 활용해서 더 좋은 팁은 또 정리해 봐야겠어요.

· 약 2분
Green

최근에 클리코딩 이나 디자인패턴 에 다시 관심을 갖고 있어요.
연차가 어느정도 됐지만 제자리 걸음을 하는 것 같다는 느낌에 기본으로 회기하는 마음이 생겨서요.

여러가지를 접하는 도중에 간과하고 허투로 지나치던 관례중에 구문 형식 이 중요하단걸 이해 했어요.

"코딩 스타일에 대해 논쟁하는 이유" 라는 번역 글에서 이해를 얻었고, 이 글은 "Why We Argue: Style" 를 번역한 내용이라고 하네요.

IDEA 에 Java Google Style 적용

  1. github 구글 styleguide 에서
  2. intellij-java-google-style.xml 파일 다운로드하고
  3. Preference > Editor > Code Style
  4. "Import Scheme" 으로 1에서 다운로드한 파일 선택

conclusion

개인적으로는 프로젝트 파일로 버전관리를 하거나, 팀원이 공유할 수 있는 위치를 통해서 공동 작업자들이 동시에 활용 할 수 있는 방법을 모색해야만 효과가 있을 것 같네요.

구문 형식의 관례를 지켜야하는 이유가 코딩은 작성보다 읽는데 더 큰 비용이 발생 한다는 것을 다시 한번 상기 해봅니다.

· 약 2분
Green

이런게 사실, 조금 쓰기 힘든 내용인 것 같아요.
개인적인 개발 환경의 문제이거나 제가 실수한 설정의 문제이거나 할까봐서, '이런 기능 오류를 이렇게 해결했다' 라고 올리는게 맞는건가 싶어서요.
그래서 아울러 말씀드리면 정답이 아닐 수도 있으니까 공식 참조문서를 찾아 보시고 stackOverflow 같은 곳도 잘 뒤져보시는 걸 우선 추천 드립니다.

증상

어플리케이션(테스트도 역시) 실행하게 되면 MapStruct 에서 generated 된 소스가 build/generated/sources/annotationProcessor/java 디렉토리에 .java 파일로 나타나게 되잖아요.
결과적으로 그것들이 "duplicate class" 라는 에러의 원인이 되는거죠.

해결

다시 한번 말씀드리지만 이건 제가 문제를 해결한 방법이지 정답이 아니라는 거...

공식 문서에 나온 대로 진행을 하시고 난 후,
build.gradle 에서

compileJava {
options.compilerArgs = [
'-Amapstruct.defaultComponentModel=spring'
]

doLast {
delete fileTree('build') { // 삭제를 하는데, build 디렉토리 중에서
include '**/*.java' // *.java 파일을 삭제
}
}
}

이렇습니다. 단순히 build 디렉토리에 java 파일을 지우는 걸로 해결 했어요.

마무리

더 좋은 방법이 있거나, 이 방법이 문제가 될 소지가 있다면 피드백 주시면 좋겠습니다.