패치 릴리스
고친 사람 github-actions[bot]
패치 릴리스는 이미 내놓은 소프트웨어에서 버그만 고쳐 다시 내놓는 일입니다. 새 기능도 쓰는 법의 변화도 담지 않습니다. 그래서 받아 쓰는 쪽은 자기 코드를 고치지 않고 새 판으로 갈아탈 수 있습니다.
쉽고 빠른 이해
무슨 일을 하는 물건인가 — 고침만 담은 새 판을 내는 일입니다. 라이브러리 2.3.1 에서 버그 하나를 고쳐 2.3.2 로 내놓는 것이 한 예입니다.
왜 이렇게 하나 — 받는 쪽은 버그 고침은 당장 받고 싶어도 새 기능까지 함께 받기는 부담스럽습니다. 새 기능이 섞이면 시험할 것이 늡니다. 코드를 고쳐야 할 수도 있습니다. 그러면 업데이트를 미루고, 미루는 동안 버그와 보안 구멍이 남습니다. 고침만 따로 내면 받는 문턱이 낮아집니다.
어떻게 도나
- 이미 낸 판에서 버그를 찾아 고칩니다
- 그 고침만 골라 담고 새 기능은 다음 판으로 미룹니다
- 버전 번호의 맨 끝 칸을 하나 올려 내놓습니다
- 받는 쪽은 번호만 보고 고침만 들었다는 것을 알아봅니다
언제 안 쓰나 — 새 함수를 더하는 변경은 마이너 릴리스로, 함수 이름이나 인자를 바꾸는 변경은 메이저 릴리스로 냅니다. 버그 고침이어도 인자를 바꿔야 하면 패치 릴리스에 못 담습니다.
대가 — 고침을 따로 골라 담는 일이 듭니다. 돌보는 판이 여럿이면 같은 고침을 판마다 옮기고 시험합니다. 버그 고침도 그 버그에 맞춰 코드를 짠 쪽에게는 동작이 바뀌는 일입니다.
상세
자동차 회사가 어떤 차의 브레이크 부품에서 결함을 찾았다고 해 봅시다. 회사는 새 모델을 사라고 하지 않습니다. 이미 팔린 차를 불러 문제 부품만 바꿔 줍니다. 운전석도 계기판도 그대로라서 차 주인은 운전을 다시 배울 필요가 없습니다.
릴리스는 소프트웨어를 다른 사람이 받아 쓸 수 있게 한 판으로 묶어 내놓는 일입니다. 판마다 2.3.1 같은 버전 번호를 붙여 어느 판인지 가립니다. 한 프로젝트도 시간이 지나면서 여러 판을 냅니다.
패치 릴리스는 그중 버그 고침만 담은 판입니다. 버그 고침은 약속한 동작과 실제 동작이 어긋날 때 실제를 약속 쪽으로 맞추는 일입니다. 「그달의 마지막 날을 돌려준다」고 해 놓고 평년 2월에 30을 돌려주던 함수를 28로 바로잡는 것이 그 예입니다. 앞의 리콜에 견주면 문제 부품만 바꾸는 일이 버그 고침이고, 운전을 다시 배우지 않아도 되는 것이 받는 쪽이 코드를 안 고쳐도 된다는 약속입니다.
이름의 패치(patch)는 해진 곳에 덧대는 천 조각을 뜻합니다. 소프트웨어에서는 고친 부분을 가리키는 말로 굳었습니다. 그 조각만 담아 내놓는 판이라서 패치 릴리스라고 부릅니다.
고침을 따로 내는 까닭
버그 고침을 따로 떼어 내놓는 까닭은 받는 쪽의 사정에 있습니다. 받는 쪽에게 새 판으로 바꾸는 일은 공짜가 아닙니다.
새 기능이 함께 오면 무엇이 바뀌었는지 읽어야 합니다. 시험도 다시 합니다. 때로는 자기 코드를 고칩니다. 그 일이 부담스러우면 업데이트를 미룹니다.
미루는 동안 옛 판의 버그와 보안 구멍도 남습니다. 고침을 새 기능과 한데 묶어 내면 고침만 받고 싶은 쪽도 새 기능까지 떠안습니다. 패치 릴리스는 「이 판에는 고침만 들었다」고 약속해서 받는 문턱을 낮춥니다.
버전 번호의 세 칸
패치 릴리스는 버전 번호와 함께 보면 또렷해집니다. 널리 쓰이는 번호 규칙 하나에서는 세 칸이 저마다 다른 릴리스를 가리킵니다.
시맨틱 버저닝(Semantic Versioning)은 버전 번호를 점으로 나뉜 세 칸으로 적는 규칙입니다. 칸마다 언제 올리는지도 정해 둡니다. 2.3.1 이면 앞에서부터 메이저 2, 마이너 3, 패치 1 입니다. 번호만 보고 이번 판에 무엇이 바뀌었는지 짐작하게 하려는 규칙입니다.
칸을 가르는 기준은 하위 호환입니다. 하위 호환은 새 판으로 바꿔도 기존 코드가 고치지 않은 채 도는 성질입니다. 이 성질이 지켜져야 받는 쪽이 안심하고 판을 올립니다.
릴리스도 올리는 칸의 이름을 따서 부릅니다. 하위 호환을 깨는 변경은 첫째 칸을 올려 메이저 릴리스로 냅니다. 하위 호환을 지키면서 기능을 더하면 둘째 칸을 올려 마이너 릴리스로 냅니다. 고침만 담으면 셋째 칸을 올리고, 그 판이 패치 릴리스입니다.
| 릴리스 | 올리는 칸 | 담는 것 | 받는 쪽이 할 일 |
|---|---|---|---|
| 메이저 릴리스 | 첫째 · 2.3.1 → 3.0.0 |
기존 코드를 깨는 변경 | 코드를 고쳐야 할 수 있다 |
| 마이너 릴리스 | 둘째 · 2.3.1 → 2.4.0 |
하위 호환되는 새 기능 | 새 기능을 쓸 때만 손댄다 |
| 패치 릴리스 | 셋째 · 2.3.1 → 2.3.2 |
하위 호환되는 버그 고침 | 판만 올린다 |
표의 아래로 갈수록 받는 쪽의 일이 줄어듭니다. 앞 칸을 올리면 뒤 칸은 0 으로 돌아갑니다. 그래서 2.4.0 에서 버그를 고쳐 낸 판은 2.4.1 입니다.
담는 것과 담지 않는 것
어떤 변경이 패치 릴리스에 들어가는지는 기준 하나로 가립니다. 기존 코드가 고치지 않고 도느냐입니다.
표에 나오는 취약점은 공격자가 파고들 수 있는 보안 구멍입니다. 취약점을 막는 고침은 버그 고침 가운데서도 대개 가장 급합니다.
| 변경 | 패치 릴리스에 | 까닭 |
|---|---|---|
| 틀린 계산 결과를 바로잡기 | ✓ | 약속한 동작으로 돌아간다 |
| 취약점 막기 | ✓ | 공격에 쓰이던 동작만 막는다 |
| 문서의 오탈자 고치기 | ✓ | 코드가 안 바뀐다 |
| 함수를 새로 더하기 | ✗ | 새 기능이다. 마이너 릴리스로 간다 |
| 함수 이름이나 인자를 바꾸기 | ✗ | 부르던 코드가 깨진다. 메이저 릴리스로 간다 |
버그를 제대로 고치려면 함수의 인자를 바꿔야 할 때도 있습니다. 그런 고침은 버그 고침이어도 패치 릴리스에 못 담습니다. 옛 함수를 남긴 채 새 함수를 더해 마이너 릴리스로 내거나, 다음 메이저 릴리스까지 미룹니다.
버그 고침이 누군가를 깨뜨릴 때
고침만 담았는데도 받는 쪽이 깨질 때가 있습니다. 「고침만 들었다」는 약속이 가장 지키기 어려운 순간입니다.
받는 쪽 코드가 버그 동작에 맞춰 짜여 있으면 버그를 고치는 순간 그 코드가 틀립니다. 앞의 마지막 날 함수로 보겠습니다. 받는 쪽 하나는 이 버그를 알고 2월이면 결과에서 2를 빼 써 왔습니다.
lastDay(2) // 30 · 고치기 전
lastDay(2) // 28 · 고친 뒤
lastDay(2) - 2 // 26 · 버그에 맞춘 코드
첫 줄은 고치기 전의 값입니다. 둘째 줄은 패치 릴리스를 받은 뒤의 값입니다. 셋째 줄처럼 2를 빼던 코드는 고친 판에서 26 을 얻습니다. 고침이 이 사람에게는 새 버그가 된 셈입니다.
새 판으로 바꾼 뒤 전에 되던 것이 안 되는 일을 회귀라고 합니다. 패치 릴리스는 회귀가 없어야 하는 판입니다.
회귀 테스트는 전에 되던 동작이 여전히 되는지 다시 확인하는 시험입니다. 내는 쪽은 패치 릴리스를 내기 전에 고침과 함께 이 시험을 돌립니다. 고침이 다른 동작을 건드리지 않았는지 내놓기 전에 알 수 있습니다.
그래도 받는 쪽 코드가 무엇에 기대는지 내는 쪽이 다 알 수는 없습니다. 변경 로그는 판마다 무엇이 바뀌었는지 적은 기록입니다. 내는 쪽은 고친 동작을 변경 로그에 적어 받는 쪽이 스스로 확인하게 합니다.
받는 쪽이 패치를 받는 방법
패치 릴리스가 받는 쪽에 들어오는 길은 판을 올리는 일을 사람이 하느냐 도구가 하느냐로 갈립니다.
의존성은 프로젝트가 가져다 쓰는 남의 코드입니다. 의존성을 내려받고 판을 맞추는 도구가 패키지 매니저입니다.
많은 패키지 매니저는 의존성의 판을 하나로 박지 않고 범위로 적게 해 줍니다. 이렇게 적은 범위가 버전 범위입니다. 예를 들어 범위를 「1.4 로 시작하는 판」으로 적었습니다. 아래는 새 판이 나왔을 때 그 범위에 드는지를 적은 것입니다.
1.4.3 // 패치 · 범위 안이라 들어온다
1.5.0 // 마이너 · 범위 밖
2.0.0 // 메이저 · 범위 밖
패치 릴리스만 저절로 들어오고 나머지는 사람이 골라 올립니다. 버전 번호의 약속을 믿고 셋째 칸만 열어 둔 셈입니다.
범위만 적어 두면 같은 프로젝트를 오늘 받은 사람과 다음 주에 받은 사람의 판이 달라집니다. 그래서 실제로 받은 판을 하나하나 적어 두는 락 파일을 함께 씁니다. 락 파일이 있으면 패치 릴리스도 저절로 들어오지 않습니다. 누군가 락 파일을 갱신해야 새 판이 들어옵니다.
이때 버전 범위는 갱신할 때 어느 판까지 올릴지를 정합니다. 범위가 「1.4 로 시작하는 판」이면 갱신해도 1.4.x 안에서만 올라갑니다. 앞에서 셋째 칸만 열어 둔 것이 여기서 쓰입니다.
고침만 골라 담는 방법
내는 쪽이 고침만 담은 판을 만들 때 걸리는 것은 코드의 시점입니다. 버그를 고친 시점의 코드에는 이미 다음 판의 기능이 섞여 있습니다.
커밋은 코드 변경 한 묶음을 기록으로 남긴 것입니다. 커밋이 차례로 쌓인 갈래가 브랜치입니다. 개발은 대개 모두가 커밋을 모으는 메인 브랜치 하나에 쌓입니다.
2.3.1 을 낸 뒤에도 메인 브랜치에는 새 기능 커밋이 들어옵니다. 그 상태에서 버그를 고치고 메인 브랜치를 전부 내면 새 기능까지 따라 나갑니다.
그래서 낸 판에서 따로 갈라진 릴리스 브랜치를 둡니다. 2.3 릴리스 브랜치는 2.3.x 판만 내는 브랜치입니다. 메인 브랜치의 새 기능이 여기에는 들어오지 않습니다.
메인 브랜치에서 먼저 고친 것을 옛 판의 릴리스 브랜치로 옮겨 담는 일을 백포트라고 합니다. 고침이 앞선 코드에서 먼저 이루어지고 옛 판으로 거꾸로 내려가기 때문에 붙은 이름입니다. 버그 고침 커밋만 2.3 릴리스 브랜치로 백포트하고 거기서 2.3.2 를 냅니다.
백포트에 흔히 쓰는 수단이 체리픽입니다. 체리픽은 커밋 하나를 골라 다른 브랜치로 옮기는 일입니다. 브랜치를 통째로 합치지 않고 고른 커밋만 가져오므로 새 기능 커밋은 따라오지 않습니다.
flowchart TD
subgraph 메인["메인 브랜치"]
M1["2.3.1 을 낸 시점"] --> M2["새 기능 커밋"] --> M3["버그 고침 커밋"]
end
subgraph 릴리스["2.3 릴리스 브랜치"]
R1["2.3.1"] --> R2["버그 고침 커밋"] --> R3["2.3.2 · 패치 릴리스"]
end
M1 --> R1
M3 -.->|체리픽으로 백포트| R2
그림에서 새 기능 커밋은 메인 브랜치에만 남습니다. 버그 고침 커밋만 점선을 따라 릴리스 브랜치로 건너가 2.3.2 가 됩니다. 이 점선이 체리픽으로 하는 백포트입니다.
메인 브랜치의 코드가 옛 판과 많이 달라졌으면 고침이 손대지 않은 채로는 안 붙습니다. 그때는 같은 고침을 옛 코드에 맞춰 다시 짭니다.
여러 판을 함께 돌보는 비용
패치 릴리스를 몇 판에나 내줄지는 내는 쪽이 정해야 할 문제입니다. 받는 쪽이 모두 최신 판을 쓰지는 않기 때문입니다.
2.3 과 2.4 와 3.0 을 쓰는 사람이 모두 남아 있으면 일이 세 배가 됩니다. 보안 구멍 하나를 막으려면 릴리스 브랜치 셋에 같은 고침을 옮겨야 합니다. 시험도 세 번, 내놓는 판도 셋입니다. 돌보는 판이 늘수록 이 일도 그만큼 불어납니다.
그래서 프로젝트는 패치 릴리스를 내줄 판과 기간을 미리 정해 둡니다. 장기 지원(Long-Term Support, LTS) 판은 다른 판보다 오래 패치를 내주기로 약속한 판입니다. 판을 자주 올리기 어려운 쪽은 이 판을 골라 고침만 받으며 오래 씁니다.
약속한 기간이 끝나는 일을 지원 종료라고 합니다. 지원이 끝난 판에는 더 이상 패치 릴리스가 나오지 않습니다. 그 뒤에 찾은 취약점도 그 판에서는 막히지 않으므로 받는 쪽은 그 전에 판을 올려야 합니다.
핫픽스와 가르기
핫픽스는 패치 릴리스와 자주 섞여 쓰이는 이름입니다. 둘은 다른 기준으로 붙는 이름이라서 한 고침이 둘 다일 수 있습니다.
핫픽스는 운영 중인 서비스의 급한 문제를 정해진 출시 일정을 기다리지 않고 바로 고쳐 내보내는 일입니다. 핫픽스는 얼마나 급한지를 보고 붙이는 이름입니다. 패치 릴리스는 판에 무엇이 들었는지를 보고 붙이는 이름입니다.
라이브러리가 급한 보안 고침을 2.3.2 로 바로 냈다면 그 판은 핫픽스이면서 패치 릴리스입니다. 거꾸로 버그 고침 몇 개를 모아 정기 일정에 맞춰 낸 패치 릴리스는 핫픽스가 아닙니다. 서비스 하나를 직접 운영하는 팀은 버전 번호를 따로 붙이지 않고 핫픽스를 배포하기도 합니다.
관련 항목
패치 릴리스와 함께 버전 번호를 나누는 릴리스
메이저 버전 · 마이너 릴리스 · 프리릴리스 · 릴리스
패치 릴리스가 따르는 버전 규칙
시맨틱 버저닝 · 버저닝 · 하위 호환 · 하위 비호환 변경 · 브레이킹 체인지 · 공개 API · 안정성 수준
패치 릴리스가 고치는 결함과 고침
버그 · 버그 수정 · 취약점 · 보안 패치 · CVE · 회귀
패치 릴리스를 내기 전후의 확인과 기록
패치 릴리스를 만드는 브랜치 작업
커밋 · 브랜치 · 메인 브랜치 · 릴리스 브랜치 · 유지보수 브랜치 · 체리픽 · 백포트
패치 릴리스를 받아 쓰는 도구와 장치
패키지 매니저 · 의존성 · 버전 범위 · 락 파일 · 의존성 관리 · 의존성 지옥
패치 릴리스를 내는 시점과 기간
릴리스 주기 · 핫픽스 · 장기 지원 · 지원 종료
패치 릴리스가 속하는 상위 분야
다른 이름: patch release · 패치 버전 · 포인트 릴리스 · point release · 버그 수정 릴리스