2023-07-22
mix.exs
파일을 수정해 의존성을 추가해서 라이브러리를 사용하다가 mix.exs
파일에서만 의존성을 제거한다. mix.lock
파일에서도 지워야하는데, 그대로 놔뒀다. 이제 mix.lock
에 있어서 다운로드하지만 사용하지 않는 쓰레기 의존성이 생겼다.
사용하지도 않는데, 구태여 받을 필요가 없다. 그런 의존성이 있는지는 deps.unlock
mix 태스크(task)에 옵션을 주면 검사할 수 있다.
mix deps.unlock --check-unused
mix.lock
파일에 사용하지 않는 의존성이 있는지 검사한다. exit code로 사용하지 않는 의존성이 지금 있는지 확인할 수 있다. 나는 script/test 스크립트에 추가했다.
테스트 방법
$ git diff
diff --git a/apps/builder/mix.exs b/apps/builder/mix.exs
index 37eee14..c1b05a7 100644
--- a/apps/builder/mix.exs
+++ b/apps/builder/mix.exs
@@ -28,6 +28,7 @@ defmodule Builder.MixProject do
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
# {:sibling_app_in_umbrella, in_umbrella: true}
+ {:decimal, "~> 2.1"}
]
end
end
diff --git a/mix.lock b/mix.lock
index 5b94297..de4125c 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,5 +1,6 @@
%{
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
+ "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dialyxir": {:hex, :dialyxir, "1.3.0", "fd1672f0922b7648ff9ce7b1b26fcf0ef56dda964a459892ad15f6b4410b5284", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo:
decimal 패키지를 추가했다. mix.exs
파일과 mix.lock
파일에 관련 정보가 추가됐다.
이 상태에서 mix.exs
파일에 설정된 {:decimal, "~> 2.1"}
코드를 삭제한다. mix.lock
파일은 수정하지 않는다. 즉 mix.exs
파일에는 없어서 사용되지 않는데, mix.lock
파일에는 설정되어 있어서 다운로드 받는 종속성을 만든 상태다.
$ mix deps.unlock --check-unused
** (Mix) Unused dependencies in mix.lock file:
* :decimal
$ echo $?
1
잘 검출된다.
참고
2023-06-24
서버를 실행하면 특정 포트(port)를 열다가 실패하는 경우가 있다. 이미 수신 대기하고 있는 포트라는 메시지가 보인다. windows에서는 netstat, findstr, taskkill 프로그램을 사용해서 특정 포트를 수신 대기하고 있는 프로세스의 ID를 찾아 해당 프로세스를 죽일 수 있다.
netstat -nao | findstr 8081
8081 포트를 수신 대기하고 있는 PID(프로세스 ID)를 찾는다.
taskkill /f /pid [찾은 PID]
해당 프로세스를 죽인다.
링크
2023-05-28
elixir 대화형 셸(interactive shell)인 iex를 사용하면 코드를 evaluation해서 모듈을 실시간으로 추가할 수 있고 실행할 수 있다. iex 프로그램을 실행한다. 함수를 실행한다. 코드를 수정한다. 다시 컴파일한다. 함수를 실행한다. 이런 빠른 이터레이션이 iex 안에서 가능하다.
간단한 코드면 괜찮은데, 긴 코드면 매번 iex 셸에서 입력하는 것도 귀찮아진다. iex 입력 히스토리를 기억해서 이전 히스토리를 뒤져 다시 실행하면 되지만 히스토리는 줄 단위로 기록된다. 여러 줄에 걸쳐 입력한 코드는 여러 번 히스토리를 뒤져야 한다.
elixir 스크립트 파일(.exs)을 만들어서 min run
으로 실행하면 된다.
query = from t in "tracks",
join: a in "albums", on: t.album_id == a.id,
where: t.duration > 900,
select: [t.id, t.title, a.title]
이런 코드를 priv/playground.exs
파일로 저장한다.
$ mix run priv/playground.exs
mix run
으로 실행한다. mix run은 애플리케이션 종속성과 애플리케이션을 시작한 상태에서 추가로 특정 코드를 실행해 준다. 긴 코드도 편하게 테스트해 볼 수 있다. Programming Ecto(2019) 책에서 배웠다.
2023-04-29
연말 정산을 하다가 암호가 걸린 PDF를 첨부해야 할 일이 생겼다. 암호를 풀고 첨부해야 하는데, macOS에서 제공하는 미리보기 프로그램으로 PDF를 열어서 암호화하지 않은 PDF로 익스포트 하는데 잘되지 않는다. 암호화가 풀린 게 아니라 내가 입력한 비밀번호를 기억해서 바로 열어주는 것 같았다.
다행히 관련 프로그램을 homebrew 패키지 매니저로 설치해 실행해서 푸는 방법을 찾았다. qpdf 프로그램을 사용하면 된다.
$ brew install qpdf
$ qpdf --decrypt --password=xxxxx encrypted-filename.pdf decrypted-filename.pdf
암호화가 풀린 PDF 파일을 손쉽게 만들었다. 연말 정산 끝~
참고
2023-02-25
파인더에서 숨김 파일이 보이지 않아 불편해 변경했다.
$ defaults write com.apple.finder AppleShowAllFiles -bool true;killall Finder
macOS에서는 프로그램 설정을 속성 목록(plist)을 편집해서 바꿀 수 있다. plist
파일을 찾아서 열어 해당 속성을 바꾸거나 셸에서 defaults
프로그램을 사용해 속성을 변경할 수 있다.
참고
2023-01-29
winget을 사용하면 windows에서 커맨드라인으로 프로그램을 설치할 수 있다. apt-get
, brew
처럼 손쉬운 설치가 windows에서 가능하다. 회사에서 코드 사이닝을 안 한 powershell 스크립트 파일을 사용 못 하게 도메인 정책으로 막아놓곤 해서 사용 못하던 chocolatey 대신 사용할 수 있다.
C:\> winget install -e --id JetBrains.dotUltimate
dotUltimate를 설치하면 너무 조용히 설치가 끝난다. 이렇게 조용할 리가? 설치 프로그램을 찾아보니 하나도 설치가 안 됐다.
C:\> winget install -e --id JetBrains.dotUltimate --override "/SpecificProductNames=ReSharper;dotTrace;dotMemory;dotCover;dotPeek;Rider /Silent=True /VsVersion=17.0"
JetBrains.dotUltimate.installer.yaml 파일을 참고해 --override
옵션을 사용해 변경했다. 디폴트로 설정한 VsVersion
이 안 맞아서 설치가 제대로 안 됐던 것 같다. 17.0
으로 변경하고 설치할 프로그램을 지정하니 잘 된다.
2022-11-19
마이크로소프트에서 만든 윈도우 터미널(windows terminal)은 셸(shell)을 호스팅하는 애플리케이션(application)이다. 즉 이 애플리케이션을 사용해 cmd, bash, powershell 등을 띄울 수 있다. 화면 분할도 지원해서 그동안 사용하던 ConEmu를 버리고 윈도우 터미널을 사용하고 있다.
C:\> winget install --id=Microsoft.WindowsTerminal -e
윈도우 패키지 매니저인 winget을 사용해 간단히 설치할 수 있다.
Win+R wt
로 실행할 수 있다. 실행 파일 이름이 wt.exe
인 걸 몰라서 한참 찾았다.
이제 git bash를 세팅할 차례다. C-, 를 눌러 설정창을 연 후 ‘+ 새 프로필 추가’ 를 클릭한다.
- 명령줄: “C:\git-sdk-64\git-cmd.exe” –no-cd –command=usr/bin/bash.exe -l -i
- 시작 디렉터리:
%USERPROFILE%
이제 윈도우 터미널에서도 git bash를 사용할 수 있다.
2022-10-16
case val do
200 -> true
404 -> true
_ -> false
end
case
문에서 200 이거나 404일 때, 검사를 한 번에 하려면 어떻게 하면 될까?
case val do
n when n in [200, 400] -> true
_ -> false
end
case val do
n when n == 200 or n == 400 -> true
_ -> false
end
when
키워드로 가드(guard)를 사용하면 된다. 다만 이걸 쓰기 위해서는 심볼을 바인딩하고 이 심볼을 when
으로 검사해야 한다. 위 코드에서 바인딩한 심볼이 n
이다. or
연산자를 써도 되고 in
연산자를 써도 된다.
참고
2022-09-17
사내 linux 가상 머신 인스턴스에 gitlab runner를 설치하려고 했을 때였던 걸로 기억한다.
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" \
| sudo bash
gitlab 저장소를 추가한다. 이렇게 해야 이후에 업데이트도 쉽게 따라갈 수 있다. sudo apt-get update
명령만 내리면 최신 버전 정보를 가져오기 때문이다.
하지만 저 명령이 이상하게 동작하지 않는다. 특정 단계에서 시간이 오래 걸리며 시간 초과 에러가 난다. 스크립트 파일을 직접 다운로드 받아서 어디서 문제가 나는지 하나씩 실행해봤다. 아무런 문제 없이 잘 실행된다. 과연 뭐가 문제일까?
환경 변수 문제였다. 프록시 서버를 통해 인터넷 접근이 가능했는데, 그 설정을 https_proxy
, http_proxy
환경 변수로 하고 있었다. sudo bash
명령은 환경 변수 유지 없이 superuser로 bash를 실행한다. 그래서 프록시 서버 세팅이 안 되니 인터넷에 접근해 파일을 받아오는 스크립트에서 시간 초과 에러가 발생한다.
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" \
| sudo -E bash
-E
옵션을 사용하면 환경 변수를 유지한다. sudo bash
를 sudo -E bash
로 바꿔주면 잘 동작한다.
링크
2022-07-16
defaults write -g ApplePressAndHoldEnabled -bool false
Android Studio IDE의 vim 플러그인 ideavim을 설치해서 사용하다 보니 키 반복 입력을 활성화하는 방법을 알려주더라. 친절하다. 터미널을 열어서 위 명령어를 입력하면 된다.
링크
2022-06-25
‘에어팟(AirPods, 2019) 2세대‘를 잘 사용하고 있다. iOS, iPadOS 14 이후로 지원되는 자동 전환을 켜서 사용하고 있었다. macOS도 지원돼서 기기간 연결이 잘 넘어간다. macOS에서 음악을 듣다가 별도 작업 없이 iOS로 듀오링고를 켜서 영어 공부를 하면 에어팟이 iOS에 연결이 되는 식으로 동작한다.
하지만 대부분의 부모가 그렇듯이 내 것인 줄 알았던 아이패드(iPad)가 자녀 교육용으로 넘어갈 때, 문제가 생긴다. 한창 코로나19 때문에 원격 수업을 많이 할 때, 문제가 발생했다. 화장실에 가려고 딸기부엉이 방 근처로 가면 원격 수업을 하는 아이패드에 내 에어팟이 연결되곤 했다. 방에선 갑자기 아이패드에서 소리가 안 들린다며 나를 찾기 시작한다.

에어팟이 연결된 상태에서 블루투스(bluetooth) 설정으로 들어간다. 연결된 에어팟 옆에 보이는 i 버튼을 누르면 상세 설정에 들어갈 수 있다. 거기서 연결 메뉴를 볼 수 있는데, 자동 연결이 아닌 마지막으로 연결된 기기일 때만 연결되는 옵션을 선택한다. 이제는 소유권이 애매해진 아이패드에서는 자동연결을 끄고 사용 하고 있다.
2022-05-01

시스템 환경설정 > 사용자 및 그룹 > 로그인 항목
목록에 보이면 클릭한 후 밑에 -
버튼을 눌러서 제거하면 된다.
macOS steam 프로그램에 환경 설정 > 인터페이스 > 컴퓨터를 시작할 때 Steam 자동 실행
옵션이 있지만 동작하지 않는다. Disable Steam auto start on Mac 글을 참고해서 자동 실행을 껐다.
2022-04-10

macOS 업데이트가 실패했다. 복구 모드로 와이파이를 잡고 운영체제를 다운로드받아 설치하는 것도 실패했다. 그냥 로컬에 저장된 복구 OS를 설치할걸. 그걸로 다시 시도해본다. 또 실패했다. USB에 macOS를 설치해 이걸로 부팅해서 설치해야겠다. 보안 칩이라니 있으니 좋겠지 하며 흘려들었던 T2 보안 칩이 부팅을 막는다.
한번 고생을 한 후에는 macOS 업데이트를 할 때, 항상 USB로 부팅할 수 있게 만들어 놓는다.
- Mac을 켠 다음 Apple 로고가 표시되면 곧바로 command(⌘)-R 키를 길게 누릅니다. Mac이 macOS 복구로 시동됩니다.
- 암호를 알고 있는 사용자를 선택하라는 메시지가 표시되면 사용자를 선택하고 ‘다음’을 클릭한 후 관리자 암호를 입력합니다.
- macOS 유틸리티 윈도우가 나타나면 메뉴 막대에서 유틸리티 > 시동 보안 유틸리티를 선택합니다.
- 인증하라는 메시지가 나타나면 ‘macOS 암호 입력’을 클릭한 다음 관리자 계정을 선택하고 해당 암호를 입력합니다.
- Apple T2 보안 칩이 탑재된 Mac의 시동 보안 유틸리티에 관하여
외부 또는 제거 가능한 미디어에서 시동 허용
라디오 버튼을 체크하면 된다.
2022-03-09
iex> match?(%{a: _}, %{a: 1, b: 2})
true
iex> match?(%{c: _}, %{a: 1, b: 2})
false
match?/2 매크로는 패턴에 매칭(matching)하는지를 boolean으로 리턴한다.
iex> match?(%{a: c}, %{a: 1, b: 2})
warning: variable "c" is unused (if the variable is not meant to be used, prefix it with an underscore)
iex:17
true
iex> c
c
** (CompileError) iex:18: undefined function c/0
매크로 안에서 심볼에 값을 바인딩 되지 않는다. context를 공유하지 않는 macro hygiene이 적용된다.
iex> match?(%{a: x} when x > 2, %{a: 4, c: 2})
true
iex> match?(%{a: x} when x < 2, %{a: 4, c: 2})
false
안에서 쓸 수 없을 것 같은 가드 절(guard clause)도 사용할 수 있다.
그래서? 단독으로 사용할 때는 시큰둥해진다. 여기에 다른 함수를 끼얹는다면?
iex> list = [a: 1, b: 2, a: 3]
[a: 1, b: 2, a: 3]
iex> Enum.filter(list, &match?({:a, _}, &1))
[a: 1, a: 3]
iex> Enum.any?(list, &match?({_, 3}, &1))
true
iex> Enum.all?(list, &match?({_, 3}, &1))
false
iex> Enum.find(list, &match?({:a, x} when x > 2, &1))
{:a, 3}
Enum.filter/2, Enum.any?/2, Enum.all?/2, Enum.find/3 등 술어(predicate)를 인자로 받는 모든 함수에 편하게 쓸 수 있다.
2022-02-20
닌텐도 스위치와 컴퓨터를 USB 케이블로 연결한다. 바로 인식하지 않는데, 별도 프로그램이 필요하다. 닌텐도 스위치 OS가 안드로이드인가보다. Android File Transfer 프로그램을 설치해야 한다.
닌텐도 스위치에서 설정 > 데이터 관리 > 화면 사진과 동영상 관리 > USB를 연결하여 컴퓨터에 복사
메뉴를 선택한다. 그 후 설치한 Android File Transfer 프로그램을 실행하면 닌텐도 스위치 디렉터리가 보인다.
macOS에서 테스트했다. windows에서도 잘 동작하지 싶다.
2022-02-13

apple 단축어 앱을 사용하면 wifi 접속 QR 코드를 만들 수 있다.
- 텍스트:
WIFI:S:와이파이이름;T:WPA;P:패스워드;;
- QR 코드 생성
- 훑어보기
QR 코드를 찍으면 wifi 접속할 것인지 묻고 접속한다. 아이폰, 갤럭시 상관없이 동작한다. 둘 다 동작한다니 표준 스팩 같은 게 있는 게 아닐까? 표준 문서는 못 찾았다. 대신 email, vCard, sms, facetime, map, calendar event, wifi network 설정이 적힌 barcode 스캐닝 라이브러리 ZXing의 Barcode Contents 문서를 찾았다.
만드는 법을 설명한 youtube를 보고 따라서 액자를 만들어봤다. 액자가 구려서 안 예쁘다. airbnb 숙소를 운영하는 것도 아니어서 사진만 찍고 폐기했다. 친척이 놀러 왔을 때, wifi 비번을 물어보면 apple 단축어 앱으로 만든 QR 코드를 보여주는 거로 충분할 것 같다. 그래도 찍기만 했던 QR 코드를 만드니 재미있었다.
PS: 사진에 찍힌 QR 코드는 와이파이 SSID와 패스워드는 임의로 만든 거니 스캔해도 쓸모가 없다.
참고
2021-12-11
"+p
커맨드로 OS 클립보드 내용을 붙일 수 있다.
커맨드를 분해하면 다음과 같다.
- ”
- +
- p
- 붙이기(paste) 동작. 복사하려면 y 키를 사용하면 된다.
set clipboard=unnamed
이 세팅으로 OS 클립보드를 같이 사용할 수 있지만 기본 세팅으로 놔두고 쓰고 있다. emacs는 OS 클립보드를 구분하지 않는데, 이참에 vim도 바꿀까 싶기는 하다.
참고
2021-11-24
map = for i <- 1..100, into: %{}, do: {i, Enum.random([false, true])}
키는 숫자, 값은 boolean인 map을 만든다. %{1 => true, 2 => false}
이런 식으로 만들어진 map이다.
value가 true인 key, value 쌍을 뽑아서 map을 만들려면 어떻게 하면 될까? Enum.filter/2
함수를 써서 value가 true인 key, value 쌍을 걸러내 보자.
map |> Enum.filter(&match?({_, true}, &1))
[
{12, true},
{38, true},
{93, true},
{53, true},
{46, true},
{23, true},
{80, true},
{96, true},
{75, ...},
{...},
...
]
Enum.filter/2
리턴 값이 list라서 map이 아닌 list가 나온다.
map |> Enum.filter(&match?({_, true}, &1)) |> Map.new()
%{
48 => true,
62 => true,
39 => true,
83 => true,
63 => true,
34 => true,
68 => true,
...
}
map에 filter를 적용하려면 list를 리턴하는 Enum.filter/2
함수 다음에 Map.new/1
함수를 사용해 list를 map으로 만들어야 한다.
erlang도 이럴까? erlang 라이브러리에는 둘러가지 않고 한방에 가는 maps.filter/2 함수가 있다.
:maps.filter(fn _, value -> value end, map)
%{
48 => true,
62 => true,
39 => true,
83 => true,
63 => true,
34 => true,
...
}
pipe 연산자에 친화적이지 않다. map을 첫 번째 인자로 받지 않아 마음에 안 들지만 잘 동작한다. 중간에 list를 생성하지 않아 훨씬 더 빠르다.
다행히 elixir 1.13.0에 추가된다. Map.filter/2
함수뿐만 아니라 Map.map/2
함수도 추가된다.
버전업이 곤란하면 구현 함수를 복사해서 사용하면 된다. 단순 :maps.filter/2
함수 호출인가 싶어서 코드를 살펴봤다. :maps.iterator/1, :maps.next/1 함수로 반복자(iterator)를 만들어서 요소들을 차례로 순회하며 filter 함수를 호출한다. 생각해보니 map 요소에 접근하는 함수 시그니처(signature)도 다르다. elixir에서는 tuple을 인자로 받는데, erlang에선 2개의 인자를 받는다. 그래서 직접 순회하며 filter 함수를 호출하게 했다.
참고
2021-10-13
defp ask_and_schedule(producers, from) do
case producers do
%{^from => {pending, interval}} ->
# ...
%{} ->
# ...
end
end
GenStage 코드를 보다가 발견했다. Map.get(producers, from)
혹은 producers[from]
으로 값을 가져오는 게 아니라 패턴 매칭을 사용한다.
Map.get/3 함수보다 빠를까?
defmodule Map do
def get(map, key, default \\ nil) do
case map do
%{^key => value} ->
value
%{} ->
default
other ->
:erlang.error({:badmap, other}, [map, key, default])
end
end
end
당연. 빠를 수밖에 없다. Map.get/3
함수를 패턴 매칭으로 구현했다. 패턴 매칭이 더 근본이다. 하지만 크게 의미있는 속도 차이는 없으니 가독성을 보고 선택하면 된다.
2021-09-29
EDATE(DATE(1969,7,20), 1)
EDATE(B1,-3)
인자로 시작일과 개월 수를 받는다. 달마다 기록하는 항목이 있을 때, 편리하게 사용할 수 있다.
PS: 앞에 e는 무슨 의미일까?