parseInt vs. Math.floor

1
2
3
4
5
6
7
8
9
10
11
let n = 1.1;
console.log(Math.floor(n), parseInt(n)); // 1 1

n = 1.9;
console.log(Math.floor(n), parseInt(n)); // 1 1

n = -1.1;
console.log(Math.floor(n), parseInt(n)); // -2 -1

n = -1.9;
console.log(Math.floor(n), parseInt(n)); // -2 -1
  • 소수점 이하를 버릴 때 Math.floor()(내림)를 주로 썼는데 음수에서 기대한 것과 다르게 동작하는 것을 알게 되었다…
  • parseInt()는 매개변수로 문자열을 받기 때문에 주로 문자열로부터 숫자를 파싱 할 때 사용하는 줄 알았다
  • parseInt()는 매개변수로 string 타입이 아니면 스트링으로 자동 캐스팅하여 그냥 숫자를 넘겨줘도 잘 동작한다
  • ⚠️ 음 근데 문자열로 된 숫자를 인풋으로 줄 때 주의해야 한다
  • parseInt나 Math.floor나 상관없이 엄청나게 큰 문자열로 된 숫자를 number 타입으로 캐스팅하는 용도로 사용하면 안 된다

참고

js scope 스코프, 렉시컬 스코프

  • 전역 변수, 지역 변수 구분하는 것이다
  • 스코프 ; 중괄호로 묶인 영역

전역, 지역 변수

지역변수는 바깥에서 사용할 수 없다
1
2
3
4
5
6
7
const f = () => {
const x = 123;
console.log(x);
};

f(); // 123
console.log(x); // 에러 ; ReferenceError: x is not defined
  • 함수 f 에 선언된 f의 지역변수 x를 바깥에서 사용하지 못하는 모습이다
전역 변수 사용
1
2
3
4
5
6
7
8
const x = 123;

const f = () => {
console.log(x);
};

console.log(x); // 123
f(); // 123
  • 반대로 함수에서는 바깥에 선언된 전역 변수 x를 사용할 수 있다
같은 변수명 일 때는 지역변수
1
2
3
4
5
6
7
8
9
const x = 123;

const f = () => {
const x = 999;
console.log(x);
};

console.log(x); // 123
f(); // 999
  • 전역 변수, 지역 변수에 동일한 이름의 변수가 있으면 가까운 변수를 참조한다

스코프 체인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let x = 1;

{
console.log(x); // 1

{
x = 2;
console.log(x); // 2

{
let x = 111;
console.log(x); // 111

{
console.log(x); // 111
x = 222;
console.log(x); // 222
}
}
}
}
  • 가깝다는 것은 일단 {} 스코프를 기준으로
  • 일단 자기 자신이 속한 스코프에서 변수 x를 찾는다
  • 없으면 상위 스코프에서 찾는다
  • 찾을 때까지 반복한다
  • 이를 스코프 체인이라고 한다
  • 근데 이 스코프에 의한 전역, 지역 변수 구분은,
  • 그러니까 내가 진짜로 참조하는 변수는 스코프를 선언할 때 결정된다 (렉시컬 스코핑)

렉시컬 스코핑

렉시컬 스코핑
1
2
3
4
5
6
7
8
9
10
11
12
13
let x = 1;

const f = () => {
console.log(x);
};

const main = () => {
let x = 999;
f(); // 1
console.log(x); // 999
};

main();
  • 선언할 때 결정된다는 것은
  • 위 코드의 결과처럼
  • 함수 f 가 선언 당시에 최상위 스코프의 let x = 1을 바라본다는 것이다
  • 함수 main을 보면 함수 f가 실행되기 전에 스코프의 let x = 999는 함수 f에 영향을 주지 않는다
  • 이런 개념들은 js를 하면서 그냥 익숙해져 있어서 용어로 설명할 수 있을 정도는 아니었다
  • 이번 기회에 js 개념, 용어들을 정리해봐야겠다

참고

js !! not not (double not)

  • 어느 날 조건문에서 !!을 본 적이 있다
  • 처음에는 왜 쓸모없이 not을 2번 썼지?하면서 리팩토링이랍시고 !!을 지워버렸다 ㅋㅋㅋ
  • 그런데 알아보니까 Boolean 타입으로 캐스팅해주는 것이었다
1
2
3
if (!obj) return; // obj가 존재하면 다음 로직 진행

// obj ...
  • 나는 보통 obj가 유효한지 체크하려고 !을 붙여서 사용하곤 했는데
  • 이미 !한 개를 붙이면 자동으로 Boolean으로 캐스팅된다
  • 거기에 한 번 더 not을 하면 not not 이라서 Boolean(obj)와 똑같아진다는 것을 알 수 있다

참고

내가 타입스크립트를 쓰는 이유

타입스크립트 입문 전

  • C, C++, JAVA를 학교 수업으로 들었다
  • 3,4 학년 때는 웹 프로그래밍을 많이 하면서 백엔드는 nodejs, 프론트는 ejs를 했었다
  • nodejs 위에서 js를 쓰면서 이것저것 쓰면서 익숙해지고 있었다
  • 자바스크립트는 공부해서 했기보다는 그냥 막히는 부분마다 검색해서 해결했었다
  • for문도 그냥 array api(foreach, map, reduce) 안 쓰고 for(;;)로 다 짜고..
  • 그당시 내가 느끼기로 자바스크립트는 C나 JAVA에 없는 괴랄한 문법이 많이 있었다
  • 그런거에 흥미를 가지면서 for(;;) 쓰는거 그만하고 js스러운 코드를 짜기 시작했었던 같다
  • 또 js는 여태껏 배워온 C나 JAVA와 다르게 타입에 전혀 얽매이지 않고, 웬만하면 에러없이 동작해서 엄청 편했다

타입스크립트 입문 동기

  • ts를 한번 쓰면 못 빠져나온다는 조언을 들음
  • (그만큼 좋다)

타입스크립트 첫인상

  • 기존에 배웠던 엄격한 언어들과는 다르게 js는 엄청 유연한? 언어였다
  • 나는 이런 특성이 편하게 다가왔다
  • 타입 때문에 골머리 아프지 않아도 돼서 뭔가 해방된 느낌이 들었기 때문이다
  • 그런데 타입스크립트라고 자바스크립트에서 굳이 타입을 붙여 써서 뭐가 좋아지는지
  • 왜 다시 역행하는지에 대해 약간 의문이 있었다
  • 실제로 시작해서도 그간 타입을 명시하지 않았던 편안함을 잃은 기분이었다
  • 타입스크립트를 쓰면…

1 + ‘1’
// ‘11’

  • 숫자+숫자로 예상했는데 실제론 숫자+문자열하는 일은 안 겪어되는 것부터 해서
  • 실행 전에 에러를 잡아 준다는데,,
  • 그거야 처음에 잘 짜면 되는 것을,, 이때는 공감하지 못했다

내가 생각하는 타입스크립트 장점

7개월 차 (20년 9월 - 21년 4월)

  • 타입스크립트는 짱이다
  • 위에서 말한 타입스크립트를 쓰는 일반적인 장점이 있지만,
  • 나는 이 장점 다 필요 없고 자동완성이 100% 보장된다는 점에서 타입스크립트를 좋아한다
  • 예를 들면 그냥 js를 쓸 때는 배열 변수에 . 찍어도 array api 자동완성이 안된다

  • 위가 js고 아래가 ts다
  • 위는 그냥 arr 변수가 배열이든 아니든 그냥 스니펫만 제공하는 반면
  • 아래 ts의 경우 배열 타입에 맞춰서 그에 맞는 array api를 자동완성해주는 모습이다
  • 이 정도까지는 그냥 코드 스니펫으로 해결할 수 있는 수준이지만
  • 이런 api 자동완성은 기본이고, 어떤 클래스나 객체를 생성해서 하위에 멤버 변수가 있을 때

  • .만 찍으면 전부 자동완성 해준다. 오타로 인해 삽질할 필요가 전혀없는거다
  • 나는 코드 쓸 때 쉬프트 누르는 것을 별로 안 좋아한다. 쉬프트를 누르면 순간 느려지고 오타도 생기기도 하고.. 답답해진다
  • c나 java를 할 때도 일단 소문자로 쓰고 나중에 rename 기능을 통해 카멜 케이스로 변경할 만큼..
  • ts를 쓰면 이런 고민이 필요 없는 게 최초 1회만 잘 작성해두면 ctrl space만으로 코드를 작성할 수 있다 ㅋㅋㅋ
  • 또 장점이라면 내가 잘못된 코드를 작성했을 때 실행도 시켜주지 않기 때문에 모르고 지나갈 수 없다
  • 오류가 생기면 어디서 오류가 생겼는지 확실히 알 수 있다!
  • 결론! 자동완성에 한번 빠지면 헤어 나올 수 없다
  • 아니 이것도 자동완성이 된다고? 하는 곳까지 진짜 다 된다
  • ts하다가 js가면 한숨이 나올 것이다

타입스크립트 더 고수가 되고 생각나면 추가해야지..

taskkill -jar

상황

  • java -jar로 runable jar을 실행할 수 있다
  • 그런데 이 jar을 cmd로 종료할 수 없을까?
  • 보통의 프로그램이라면 taskkill /F /IM프로세스 이름을 통해 종료할 수 있다
  • 근데 java -jar 실행한 앱의 경우, 프로세스명이 java.exe, javaw.exe 정도이다

해결

  • jps 명령으로 .jar 파일 이름을 통해 PID를 얻을 수 있다
  • jps는 jdk가 설치되어있고 환경 변수만 잘 설정되어있다면 그냥 동작한다
example.bat
1
2
rem example
for /f "tokens=1" %i in ('jps -m ^| find "myApp"') do ( taskkill /F /PID %i )
  • find “myApp” 부분에 jar 파일 명을 적으면 된다
  • jar 파일명을 완전히 적지 않아도 된다

참고

gradle build 특정 jdk 버전으로

상황

  • 내 컴퓨터에는 여러 가지 자바 버전이 설치되어있다. (jdk1.8.0_281, jdk-13.0.2)
  • 자바 버전 1.8에서 동작하는 runable jar 파일을 만드는데, 1.8에서 실행이 안되었다
  • 그래들 프로젝트이다
  • 분명 프로젝트 세팅은 1.8로 되어있는데 막상 빌드 결과물 jar이 1.8에서 실행이 안되었다
  • 그런데 13에서 실행이 되는 것을 확인하고, 빌드가 다른 자바 버전으로 되는 것을 확인했다
error message
1
Error: A JNI Error has occurred, ... has been compiled by a more recent version of the java runtime

해결

1
gradlew build -Dorg.gradle.java.home="C:\Program Files\Java\jdk1.8.0_281"
  • 빌드할 때 자바 홈을 명시해 주었더니 해결되었다

참고

code . 현재창으로 열기

1
2
cd project
code .
  • vscode 내부 터미널에서 cd 명령을 통해 다른 폴더로 이동한다
  • 해당 폴더를 code .으로 열 수 있다
  • 이 경우에는 새창으로 해당 폴더가 열린다
1
code . -r
  • -r옵션을 붙여주면 현재 창을 통해 열리게 된다
  • 이 옵션을 자주 쓸 것 같다

참고

정규식으로 숫자를 포함하지 않는 것만 가져오기

1
2
3
4
5
6
7
8
9
const nickNameList = ['chinsung', 'chinsun99999999', '2021', 'regexp'];

const noNumber = nickNameList.filter((nickName) => {
const hasNumber = /\d/;

return !hasNumber.test(nickName);
});

console.log(noNumber);
  • 배열에서 숫자를 포함하는 값만 뽑아보자
  • 또는 숫자를 포함하는 값만 뽑아보자

git submodule 중첩된 깃 사용하기

  • 깃 아래 깃..!

상황

1
2
3
4
5
super
├── client
│ └── 1
└── server
└── 1
  • 이런 구조의 프로젝트가 있다
  • super에서 git init 하여 프로젝트를 관리하다 보면
  • 깃 히스토리가 client 내용, server 내용이 합쳐져 있다
  • 구분하기 위해 커밋 메시지를 적을 때도 어떻게 적어야 할지 망설이게 된다
  • 그래서 super, client, server에 모두 git init하고
  • super에서는 client와 server를 gitignore에 추가하고 사용해봤는데,
  • 이것도 좋은 방법은 아닌 것 같았다
  • 그래서 nested git repository 라는 키워드로 검색했고 submodule 이라는 것을 사용하면 된다는 것을 알게 되었다
  • submodule을 사용하면 super 디렉터리에서 명령어 하나로 하위의 서브모듈 깃 프로젝트를 업데이트(pull) 할 수 있다

공부

  • 생활코딩님의 강의를 보고 공부했다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 전제조건
// 명령어를 실행하는 위치는 super 디렉터리이다
// submodule로 추가할 프로젝트(client, server)는 모두 git init으로 초기화된 깃 프로젝트이며 원격 저장소에 올라가 있어야 한다

// submodule 추가 // 처음 super에 서브모듈을 추가하는 과정이다
git submodule add {repo url} 디렉터리명

// 이후 다른 컴퓨터에서 super를 클론하는 경우
git clone {super-repo-url} // git clone https://github.com/chinsun9/super.git

// 하위 서브 모듈은 클론되지 않는다
// 서브 모듈도 가져오기
git submodule init
git submodule update

// 위 명령은 하위 모든 서브 모듈을 가져온다
// 특정 서브 모듈만 가져오고 싶다면
git submodule init {submodule명}
  • super에서는 하위 서브모듈의 변경을 감지하고,
  • 하위 모듈의 해시(특정 버전을 가리키는)를 기억하는 방식으로 관리된다
  • git submodule update 명령을 사용하여 super에서 저장하고 있는 특정 버전의 하위 모듈로 업데이트한다
  • git submodule update --remote 명령을 사용하여 각 하위 모듈을 최신 상태로 업데이트할 수 있다
  • 여러 컴퓨터에서 작업할 때 super를 클론하고 git pull && git submodule update 명령으로 한 번에 가져올 수 있겠다!

발생한 문제

1
2
3
4
5
6
git submodule update --remote

fatal: Needed a single revision
Unable to find current https://github.com/chinsun9/my-submodule/master revision in submodule path 'lib'
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
  • git submodule update --remote 명령을 실행하니 이런 에러가 발생했다
  • 나는 submodule을 알기 전에 그냥 하위에 또 다른 깃 디렉터리를 사용하고 있었는데,
  • 그 상태 그대로 submodule add하고 사용했더니 위 에러가 발생했다
  • 해결방법은 기존 하위 레포를 삭제하고 다시 git submodule update --remote를 실행한다
1
2
rm -rf lib/
git submodule update --remote
  • 혹시 특정 브랜치에 대해 가져오고 싶으면 .gitmodules 파일을 열고 branch 값을 명시해준다
1
2
3
4
5
6
7
8
9
10
// 수정 전
[submodule "client"]
path = client
url = https://github.com/chinsun9/my-submodule

// 수정 후
[submodule "client"]
path = client
branch = mybranchname // 추가됨!
url = https://github.com/chinsun9/my-submodule

참고