[번역] 레일스를 계속 버전업하려면 필요한 것

원래는, RubyWorld Conf에서 이런 내용으로 발표하고 싶었지만, CFP에 떨어져서 Qiita에 적습니다.

Rails는 나름 학습 코스트가 있지만, 익숙해지기만 하면 기본으로 편리한 것이 갖추어져 있기도 하고 서드파티 라이브러리도 풍부하고 지금까지 나온 것 중에서는 가장 편리한 웹 애플리케이션 프레임워크라고 생각합니다.
그래서 최근 스타트업 업계에서는 레일스로 개발을 시작한다는 이야기를 자주 듣습니다. (어디까지나 들은 이야기입니다.)
하지만, 레일스 본체에 점점 새로운 요소가 들어오기 때문에, 버전업의 사이클이 꽤 빨라서 거기에 따라가는건 꽤 힘듭니다.
레일스로 개발을 하는 경우, 일단 레일에 오르면 프로덕트가 죽을 때까지 달릴 각오가 필요합니다. (시속 60km가 넘으면 폭발함.)
그걸 제일 먼저 이해해 두지 않으면, 아차 하는 사이에 레거시화되어 유지보수의 힘듦이 점점 늘어납니다.
뭐, 레일스 3.1 이후에 개발을 시작했으면, 비교적 덜 아프게 끝납니다마는….

에초에 왜 버전업을 해야만 하냐면, 물론 보안문제가 있겠습니다만, 새로 들어온 편리한 기능이 빠르게 업데이트되는 gem을 곁눈질만 하면서 개발하는 것이 심리적으로 괴로운 게 상당히 큰 이유기도 합니다.

이런 이유로, 레일스로 개발을 하는 이상, 부지런히 새로운 버전을 올리고 싶지마는, 그러기 위해 해두지 않으면 안 되는 것이 몇가지 있습니다.

테스트 작성

먼저 제일 처음으로, 무조건 테스트 코드가 필요합니다. 만약 테스트 코드가 전혀 없다면, 레일스 버전을 올리는 것은 불가능에 가깝습니다.
지금 테스트 코드가 없는 프로젝트라면, 열심히 적을 수밖에 없습니다만, 그게 매우 괴롭습니다….

그렇게 되지 않기 위해서도, 적당히 땡땡이치지 말고 테스트코드를 적어라. TDD 해라. [1]
보다 좋은 테스트 코드의 분류방법이라던가, 테스트 설계라던가 이것저것 있겠지만은, 일단은 적어라.
최악의 경우, 잘 커버 못하는 테스트라도 좋아. 없는 거보단 나으니까.
레일스는 그런 프레임워크니까 테스트를 적어야 해. 빨리 릴리즈해서 개선 사이클을 돌리고 싶은 건 알겠지만, 테스트를 적는 것은 최소한으로 필요한 거니까 꼭 적어라.

버전업이라는 관점에서 보면, 가장 필요한 것은 feature spec입니다.
라고는 해도, feature spec에서 상세한 예외를 전부 커버하는 것은 무척 코스트가 필요합니다.
적어도, 코어기능의 비정상동작과 자주 있는 에러나 업무 예외 등등이 필요합니다.
중요한 기능, 까다로운 기능의 동작을 확실히 파악해두는 것이 좋습니다.
어떻게 해도 공수가 맞지 않거나, 자동화가 어려운 부분도 있을 수 있기 때문에, 수동 테스트할 테스트 케이스도 준비해 둡니다.

세세한 행동의 변화로 생기는 버그를 잡기 위해 단위테스트도 중요합니다.
TDD 하기 쉬운 곳이므로, 처음부터 확실히 테스트를 적어서 높은 커버리지를 유지해야 합니다.

테스트가 잘 갖추어져 있어 대체로 잘 실행된다던 가는 테스트를 실행하면 알 수 있는 다음 단계입니다.

버전업 브랜치를 준비함

레일스의 버전업의 흐름은 대체로 밑에 있는 것과 같습니다.

  1. Gemfile을 편집해 버전을 변경함.
  2. bundle update로 의존관계의 해결에 실패한 gem을 버전업함.
  3. 막힌 gem의 저장소에서 대응 브랜치가 없는지 탐색.
  4. 대응 브랜치등이 없었으면 (10 이후의 후반 참조)
  5. bundle update가 끝났으면 rake rails:update를 실행
  6. config 파일 등의 diff를 확인해서 영향이 없을 것 같은 부분을 덮어 씌워감(잘 모르는 부분은 보류)
  7. config 파일 등의 변경 점은 릴리즈노트나 레일스 가이드를 확인
  8. config 파일의 업데이트가 끝났으면 테스트를 실행함.
  9. 테스트케이스가 끝까지 실행될 때까지 에러를 계속 잡음.
  10. 막힌 gem이 있으면, 다시 저장소로 가서 조사함.
  11. 사용하는 gem의 버전이 x.y.z의 y이상의 부분이 변경되었으면, gem의 changelog를 확인
  12. 실행해서 빨갛게 되는 테스트케이스를 계속 고치면서, deprecation들을 막 수정함.
  13. 전부 테스트 케이스를 통화했으면, 수동으로 남은 부분의 테스트를 함.

뭐 대체로 이런 느낌의 일을 합니다.
이런 건 한 번에 다하는 것은 불가능하므로, 우선 브랜치를 활용해, master의 개발을 계속하면서 자주 리베이스해서 쫓아갑니다.

이 작업은 새로운 레일스의 릴리즈 타이밍이 아니더라도, 손이 비면 조금씩 해두면 부하가 분산되어 조금 괜찮습니다.
특히 엣지 레일스에서 움직이는 브랜치를 준비해두면, 버그가 발생하기 쉬우므로 Pull Req의 찬스가 늘어납니다.

gem이 새로운 레일스에 대응하지 않거나 버전이 충돌하는 경우

레일스로 개발하면서 서드파티 gem을 사용하지 않는 경우는 거의 없으리라 생각합니다.
그러므로, 레일스의 버전을 올릴 때는 gem의 버전도 올릴 필요가 있습니다.
평소에 조금씩 bundle update해두면 영향이 적게 끝납니다.

레일스에 맞춰서 gem을 갱신하는 경우, 몇 가지 해야 하는 단계가 있으므로 소개해 보겠습니다.

대응 브랜치를 찾음

먼저, 제일 좋은 방법은 아까 적은 것처럼, 개발 저장소에 대응 브랜치가 없는지 찾는 것입니다.
최근의 gem은 대체로 GitHub에 저장소가 있으므로, 거기서 브랜치를 찾읍시다.
있으면 Gemfile를 수정해 거기를 보도록 합니다.
조금 있으면 릴리즈 버전에 통합될 수 있으므로, 브랜치 버전을 사용하고 있다는 것을 잊어버리지 않도록 합니다.

issue나pull req의 상태를 확인함

대응 브랜치가 없는 경우, issue나 pull req를 확인합니다.(브랜치가 있어도 기존의 버그가 존재할 가능성이 크므로 확인해 두는 것이 좋습니다만..)
대응하기 위한 issue나 pull req가 있고, 잘 진행되고 있는 것처럼 보이면, 당분간 기다려도 됩니다.
물론, 직접 고쳐서 공헌할 수 있을 것 같으면, 끼어들어도 괜찮지만, 중간에 끼어드는 것은 꽤 어려울 것입니다.
급하게 버전업해서 동작확인 하고 싶은 경우는, 그 pull req를 watch해두어 상태를 모니터링할 수 있게 해두어서, pull req되어진 패치를 적용해서 잠정적으로 이용하는 것도 좋습니다.
본체에 마지 되었으면, 본체의 저장소로 교체합니다.

규모가 크고 메이저한 gem의 경우, 이 방법을 쓸 때가 많습니다.

fork하거나 버리거나

마이너한 gem이나 규모가 작은 gem의 경우 버전업 대응 issue가 없는 경우도 자주 있습니다.
대충 코드를 읽어봐서, 직접 고칠 수 있을 것 같으면 fork 해보는 것도 좋습니다.
우선, 버전 제약을 고쳐서 그냥 인스톨해 봅니다.
작은 gem이라면 이것만으로 움직이는 경우도 많습니다.
그 경우, 의존 버전을 고쳐서 pull req를 보내 둡니다.
당분간 자신의 fork 버전을 사용하다, 머지되면 본체로 돌립니다.

이 경우, gem 측의 테스트코드가 없으면 다소 귀찮아질 가능성이 있습니다.
미묘한 차이에 의한 버그가 발생했을 때 원인이 알기 어려워집니다.
그 gem에 계속 신세를 질 생각이라면, 테스트 코드를 정비해둘 필요가 있을지도 모릅니다.

버전을 올린 것만으로 움직이지 않은 경우, 코드를 해석해서 스스로 어떻게 하지 않으면 안됩니다.
수정하면 일단 pull req를 올려둡니다.
마지 되었으면 ㅊㅋㅊㅋ.

하지만, 개발이 활발하지 않은 gem이라던가 면, 전혀 머지되지 않을 가능성도 있습니다.
그 경우, 당분간 fork 버전을 계속 사용할 각오가 필요합니다.
최악의 경우, 자신이 계속 사용하는 동안, 스스로 어떻게 해나갈 수밖에 없게 됩니다.
그 코스트를 감당할 수 없으면, gem을 버리는 결단을 하게 됩니다.

버리고 어쩌느냐 고하면, 비슷한 기능을 가진 대체 gem을 찾거나 만들거나 입니다.
어느 쪽을 선택할지는 용도의 범용성 나름입니다. 흔히 있는 기능이라면 대체 gem을 찾을 가능성이 크므로, 어떻게 될수도 있습니다.
그렇지 않은 경우, 스스로 직접 만드는 수밖에 없습니다.
아니면, 본가에서 대응하던, 대체 gem이 나올 때 까지 기다리던가 뿐입니다.

실제로, 사용 중인 gem의 대응 상황이 버전업의 장벽이 되는 경우가 꽤 있습니다.
여기에 걸려서 계속 딜레이하면, 버전업의 기회를 놓쳐서 레거시의 어둠에 빠져버릴 수도 있습니다.
어딘가에 전환기를 두는 것도 중요합니다.
그러기 위해서는, 사용 중인 gem의 코드를 읽어서, 무엇을 하고 있는지 이해하는 것도 중요합니다.

정리

레일스를 버전업해 건전한 상태를 유지하려면, 결국 이것저것 하지 않으면 안됩니다.
테스트 코드를 유지보수해가면서, 새로운 레일스가 나오면 확실히 릴리즈노트를 확인해 변경 점을 파악하고, 이상한 동작을 발견하거나 생각한 대로 움직이지 않는 경우에 레일스의 소스 코드를 읽고 사용 중인 gem의 소스 코드를 대충 파악해두어 최악의 경우 스스로 유지보수 할 수 있을 것.
이런 것들을 항상 할 수 있도록 준비해두어야 합니다.
요점은, 자기가 쓰는 도구들은 제대로 파악해 두어야 한다는 것입니다.

물론 완전히 전체를 파악하는 것은 현실적으로 무리입니다.
포인트만 파악해두어, 무슨 일이 있을 때 읽으면 알겠지 싶은 정도로만 해두는 것이 중요합니다.
평소에 궁금했던 코드를 가볍게 읽어두거나, 새로운 gem을 사용하기 전에 간단히 구현을 확인해두면, 무슨 일이 있을 때 빠르게 일할 수 있습니다.

그리고 레일스를 확장하지 않으면 안 될 때에는, 무리한 확장은 가능하면 피하는 것이 좋습니다. 레일에서 떨어질 때에도, 무리한 탈선은 하지 않도록 합시다.

레일스 뿐만 아니라, 애플리케이션을 개발하고 운용해 나아가려면, 버전업을 대응하기 위한 “각오”가 필요합니다.
“각오”는 암흑의 광야에 나아갈 길을 개척한다! [2]
준비하자!

http://qiita.com/joker1007/items/82db0b5d1a7f2572710b
[1]: 이단락만 갑자기 반말로 하네요. 원문에 충실하려고 그냥 두었습니다.
[2]: 죠죠의 기묘한 모험에 나오는 대사입니다.

510 views