FE 개발자와 터미널 - 02

터미널 사용과 데이터 랭글링

이전 글에서 살펴봤듯이, 일상 업무에서의 적극적인 터미널 사용은 단순 반복 작업을 논리 구성작업으로 바꾸려는 시도입니다. 이는 사람이 할 일을 컴퓨터에게 시킨다는 점에서 자동화입니다. 컴퓨터 안에서 모든 자원은 데이터라는 점과 한 데이터를 원하는 포맷으로 탈바꿈 한다는 점에서 데이터 랭글링이기도 합니다. 이번 글에서는 현실적인 사례를 들어, 어떻게 일상 업무에 자동화와 데이터 랭글링이 적용될 수 있는지 살펴보는 것을 목표로 합니다.

데이터 랭글링이란?

기존 데이터를 원하는 형식으로 재구성하는 작업을 데이터 랭글링data wrangling 또는 데이터 먼징data munging이라고 한다.

데이터 사이언스 분야에서 주로 사용하는 용어이며, 사이즈가 큰 데이터 데이터 랭글링을 위해서는 pandas 등 별도의 툴을 사용하는 것이 좋다.

실제 사용 예시

웹 호스팅 서버 매뉴얼 오퍼레이션

터미널 문해력은 VM 또는 컨테이너에 직접 접속해야 하는 상황에서 더욱 빛이 납니다. CI/CD 툴의 발전으로 일반적인 상황에서 FE 개발자가 직접 웹사이트가 호스팅되고 있는 서버에 접속할 일은 없지만, 다음 상황에서는 여전히 매뉴얼 오퍼레이션이 필요합니다:

  • 레거시 시스템을 운영하거나,
  • 비정상적인 상황에서 긴급 대응이 필요한 경우,
  • 또는 CI/CD 파이프라인 구축 시 의도한 대로 파이프라인이 정상 동작하는지 확인하기 위해서

최종 호스팅 서버에 직접 접속하기도 합니다.

가정: 레거시 시스템의 nginx.conf를 수정하고 싶다.

우선, 이런 일은 생기지 않아야 합니다. config 파일을 서버에 붙어 직접 수정해야 하는 일이 생겼다면, CI/CD 파이프라인을 정상화의 우선순위를 높여 피어 리뷰를 통해 휴먼 에러를 방지하세요.

반드시 매뉴얼하게 수정해야 하는 상황이라면, diff 명령어를 확인해서 변경사항을 확인해보세요.

bash
# nginx 설정 파일이 있는 곳으로 이동
cd /etc/nginx

# 매뉴얼 오퍼레이션을 진행할 때는 항상 중요 파일을 백업하세요.
cp nginx.conf nginx.conf.bk

# 선호하는 에디터를 통해 설정 파일을 변경하세요.
vi nginx.conf

# 혹시나 실수로 원하지 않은 변경이 가해지진 않았는지 확인하세요.
# diff 명령어는 두 파일 사이의 서로 다른 라인을 출력해줍니다.
diff nginx.conf.bk nginx.conf

# nginx 재시작을 합니다.
systemctl restart nginx.service
## 또는
service nginx reload
## 또는
nginx -s reload

눈으로 확인하는 것만으로는 안전하지 않습니다. 매뉴얼 오퍼레이션 만으로도 충분히 위험하니, 컴퓨터에게 비교를 시켜 정말로 원하는 변경만 되었는지 확인하세요.

가정: 로그를 점검하고 싶다.

웹서버에 접속한 김에 로그까지 보도록 합시다. 로그는 보통 /var/log/ 위치에 저장됩니다.

  • 404 상태코드는 얼마나 많을까?
bash
# nginx 로그를 보고 싶다면, 다음 위치로 이동합니다.
cd /var/log/nginx

# 액세스 로그 중 '500'이라는 문자를 포함하는 로그를 출력합니다.
grep 404 access.log
## 또는
cat access.log | grep 404

404 상태 코드를 받은 요청에 대한 엑세스 로그를 쉽게 볼 수 있습니다. 간단한 통계를 통해 엄격하진 않지만 대략적인 서비스 상황을 알 수도 있습니다.

bash
# 액세스 로그의 라인 수를 얻고,
cat access.log | wc -l
# 404 응답의 라인 수를 얻으면 404 응답의 비유을 알 수 있습니다.
cat access.log | grep 404 | wc -l

404 코드 뿐만 아니라 주요 점검 대상들에 대해서 간단한 질의를 할 수 있습니다.

bash
## 권한 없는 요청이 얼마나 자주 일어나고 있나요?
cat access.log | grep 401
## 특정 날짜의 로그를 보고 싶은가요?
cat access.log | grep 2014/11/27
## 특정 날짜에 일어난 POST 요청 로그를 보고 싶은가요?
## 필터링은 원하는 만큼 추가할 수 있습니다!
cat access.log | grep 2014/11/27 | grep POST
  • 내 웹서비스가 제대로 요청을 받고 있는건지 알고 싶다.

로그는 파일 형식으로 저장되고, 새로운 로그는 한 파일에 이어쓰기 형식으로 더해집니다.

bash
# 로컬 머신에서 일정한 주기로 새로 설정한 웹서비스에 질의를 보낼 거에요.
watch curl 'http://my-new-server'
# 각 요청이 얼마나 걸리는지 알고 싶어 time 명령어도 써보겠습니다.
watch time curl 'http://my-new-server'
## 오! 간이 응답속도 모니터링으로 쓸 수도 있겠네요~
bash
# 서버에 접속해 액세스 로그가 잘 쌓이고 있는지 확인할 거에요.
## -f / --follow 옵션을 통해 파일이 커지는 것을 따라가며 출력할 수 있습니다.
tail -f /var/log/nginx/access.log

예시는 모두 nginx 서버의 로그였지만, docker logs 또는 kubectl logs 에도 똑같이 사용할 수 있는 패턴을 소개드렸습니다. 로그 스트림에 숨어 있는 정보를 grep으로 찾아내보세요!

UI 라이브러리의 인터페이스 변경 추적

빠른 개발을 위해 UI 라이브러리 하나를 채택해 사용하고 있다면, 라이브러리의 변경을 추적하는 일은 중요합니다. 더 빠르게 새로운 컴포넌트나 기능을 사용하고 싶을 수도 있고, 이미 사용하고 있는 컴포넌트에 브레이킹 체인지가 있어 대응해야 할 수도 있습니다.

사용하는 라이브러리에서 항상 디테일한 체인지로그를 제공하면 업데이트를 적용하는데 큰 도움이 될 것입니다. 하지만, 다음의 경우가 있을 수 있습니다:

  • 체인지로그가 충분한 정보를 주지 않거나, 존재하지 않는다.
  • 내가 사용하는 컴포넌트에 대해서만 변경내역을 알고 싶은데, 체인지로그가 너무 많고 디테일하다.

그렇다면 다음 명령어를 참고해, 원하는 변경사항만 알아보세요!

bash
# naive-ui 를 예시로 사용합니다.
git clone https://github.com/tusen-ai/naive-ui.git

cd naive-ui

# 현재 사용하고 있는 버전과 업데이트 타겟이 되는 버전이 태깅되어 있는지 확인하세요.
## 예시에서는 v2.0.0 에서 v2.3.0 으로 업데이트하려는 상황을 가정합니다.
git tag

# git-diff를 통해 어떤 변경이 있었는지 보려고 했는데, 너무 많은 변경이 있었습니다!
## git-diff의 출력이 무려 19182 줄이나 되네요!
## 더 좋은 방법이 필요합니다.
git diff v2.0.0 v2.3.0

# 이번엔 파일 이름과 변경 상태만 봅시다!
git diff --name-status

# output:
## ...
## A       src/ellipsis/src/index.tsx
## A       src/ellipsis/src/styles/index.cssr.ts
## A       src/ellipsis/styles/dark.ts
## A       src/ellipsis/styles/index.ts
## A       src/ellipsis/styles/light.ts
## D       src/empty/demos/index.entry
## M       src/empty/src/styles/index.cssr.ts
## M       src/empty/styles/_common.ts
## M       src/form/demos/enUS/async.demo.md
## M       src/form/demos/enUS/custom-rule.demo.md
## ...

# 여전히 너무 많습니다. 무려 461개의 파일이 변경되었나봐요.
## .md 로 끝나는 마크다운 파일들은 필요 없다는 것을 깨달았습니다.
## 그리고 path에 'demo'나 'tests'가 들어가 있는 파일들도 보고 싶지 않네요.
## -v / --invert-match 옵션을 사용해 원하지 않는 파일을 제외합니다.
git diff --name-status | grep -v .md | grep -v demo | grep -v tests

# 그리고 이 중에서 새로 더해지거나 없어진 파일은 관심 없고, 변경이 이루어진 파일만 보고 싶어요.
git diff --name-status | grep -v .md | grep -v demo | grep -v tests | grep '^M'

# 이렇게 하고 나니, 111 줄의 파일만 남았습니다!
## 이 정도 길이의 목록에서는 원하는 파일만 남기고 직접 삭제할 수 있겠네요!
## 우선 이 파일들의 path만 저장하도록 합시다.
## awk '{print $2}' 는 각 라인에서 공백 문자로 나누었을 때 두번째 토큰만 출력합니다.
git diff --name-status | grep -v .md | grep -v demo | grep -v tests | grep '^M' | awk '{print $2}' > interesting_files

# 이렇게 얻은 interesting_files 목록을 좀 더 다듬은 후에,
## 코드 레벨에서 어떻게 바뀌었는지 살펴보아요.
## 우선 관심 대상인 파일들을 워크스페이스로 가지고 옵시다.
git checkout v2.3.0

# xargs를 통해 git diff 명령어에 관심있는 파일 목록을 인자로 넣어줍니다.
cat interesting_files | xargs git diff v2.0.0 v.2.3.0

시맨틱 버저닝을 준수하는 라이브러리에서는 이렇게까지 볼 필요는 없습니다. 하지만 아직 0.x.x 를 사용하는 라이브러리 또는 시맨틱 버저닝을 느슨하게 사용하는 라이브러리라면, 메이저 버전이 바뀌지 않아도 브레이킹 체인지가 있을 수 있습니다. 이런 경우가 생기면, 관심있는 컴포넌트에 대해서 어떤 변경이 있었는지 코드레벨에서 살펴보는 건 어떨까요?

복수의 (n > 5) 리포에 pre-commit 정책을 적용시키기 위한 husky 설치

수많은 리포를 관리하고 있다면, 공통 설정을 추가하는 것이 곤혹스러울 수 있습니다. 공통 설정의 예로는 다음의 작업이 있을 수 있습니다:

한두 개 리포에 위의 작업을 하는 것은 간단하고 오래 걸리지 않습니다. 하지만 수 많은 리포를 관리하는 입장에서는, 결과물의 임팩트에 비해 많은 리소스를 사용해야 하고 대단히 지루한 작업을 해야 합니다. 이럴 경우, 작업을 간단한 쉘스크립트로 자동화해보는 것은 어떤가요?

  1. 기본 템플릿은 이렇습니다:

    bash
    #! /bin/bash
    ## 이 라인은 shebang이라고 합니다.
    ## 어떤 인터프리터를 사용하는지 지정할 수 있습니다.
    
    # 관리 대상인 리포의 목록을 만듭니다.
    repos=('repo1', 'path/to/repo2', 'repo3')
    
    ## 리포 목록을 순회하며 원하는 작업을 할 것입니다.
    for repo in "${repos[@]}"; do
      echo $repo
      cd $repo
    
      # 이 위치에 공통 작업을 넣으시면 됩니다!
    
      # `cd -`는 바로 이전에 있었던 디렉토리로 위치를 옮겨줍니다.
      cd -
    done
    
  2. 필요 툴을 사용해 필요한 작업을 추가합니다.

    코드 수정이 아니라면, 대체로 cli 툴을 사용해 작업 가능합니다.

    • git
    bash
    # 미처 커밋 못한 작업분이 있다면 미리 stash 해놓습니다.
    git stash
    
    # 변경의 베이스를 설정합니다.
    git fetch origin develop
    git checkout origin/develop
    
    # 작업을 위한 브랜치를 생성합니다.
    git checkout -b chore/update-commitlint
    
    • nvm / yarn / npm
    bash
    nvm use 16 # 사용하는 node 버전 설정
    yarn add -D husky @commitlint/config-conventional # 원하는 패키지 설치
    
    # 미리 작성해뒀거나, 다른 리포에서 성공적으로 동작하는 config 파일을 작업하는 리포에 복사합니다.
    cp path/to/config/commitlint.config.js ./commitlint.config.js
    
    bash
    # 작업한 파일을 커밋해줍니다.
    git add package.json yarn.lock commitlint.config.js
    git commit -m "chore: add husky and basic commitlint to enforce commit policy"
    
    # 해당 커밋에 대한 PR을 작성합니다.
    gh pr create \ # 참조: https://cli.github.com/manual/gh_pr_create
      -a @me \
      -r {리뷰어 ID} \
      -t "chore: pr에 어울리는 타이틀" \
      -b "Refer to {wiki document link} for detail explanation" # 미리 작성해둔 pr 본문이 있다면, -F 플래그를 이용할 수도 있습니다.
    

각 파트를 주석처리해가며, 의도했던 변화가 일어났는지 확인하며 작업하는 것을 추천합니다.

마치며

무엇보다, 터미널에서 명령어들의 연계로 문제를 해결하는 것은 재밌습니다. 새로운 조합을 찾고 그 강력함에 감탄을 느낄 때, 마치 철권과 같은 액션 대전 게임에서 새로운 연속기를 찾아낸 것 같은 기분이 듭니다. 개발 인생 내내 계속 마주칠 터미널이라면, 조금의 시간을 들여 자신만의 연속기 레파토리를 만들어 나가시는 건 어떤가요?