오픈소스 컨트리뷰톤 후기 (Flutter Contribution)
Table of Contents
6주간 오픈소스 컨트리뷰톤에 참여하여 Flutter 프로젝트에 기여한 경험을 공유합니다. 🏃♂️🏃♀️
오픈소스 컨트리뷰톤이란?
오픈소스 컨트리뷰톤이란 국가기술정보통신부에서 운영하는 공개 SW 사업으로, 홈페이지에서 다음과 같이 설명하고 있습니다.
컨트리뷰톤(Contributhon)은 ‘오픈소스 기여(contribute)’와 ‘마라톤(marathon)’의 합성어.
선배 개발자의 가이드와 함께 참여ㆍ공유ㆍ개방ㆍ협업의 오픈소스 프로젝트를 함께하고, 오픈소스 개발에 대한 진입장벽을 뚫어 다양한 기여(contribution)를 직접 경험하는 프로그램.
양적인 기여뿐 아니라 과정에 중점을 두고, 코드 기여뿐만 아니라 코드리뷰, 테스트, 버그리포트, 질문, 기능제안, issue댓글, 건의, 문서작성 등 다양한 작업으로 오픈소스 문화에 기여.
전체 6주간 진행되며, 총 26개의 프로젝트가 있고 참여하기 위해선 희망하는 프로젝트를 1, 2, 3순위로 선택하여 신청해야합니다. 2020년 기준으로 전체 프로젝트 목록은 여기에서 보실 수 있습니다. 최근에 Flutter에 한참 빠져있었기 때문에 1초의 망설임도 없이 Flutter Moum 이라는 프로젝트를 1, 2, 3순위로 모두 선택하여 지원하였고, 무사히 엄청난 경쟁률을 뚫고 멘티로 선정 될 수 있었습니다. 🎉🎉
사전교육
OT를 시작하기 전에 Git과 Contribution 대한 사전교육을 선착순으로 신청받아 진행하였는데, 강의는 리얼 리눅스의 송태웅 강사님이 진행해 주셨습니다. (수업자료는 여기서 보실 수 있습니다)
선착순에 늦어 후반부 수업밖에 듣지 못했지만, Git의 History를 변경할 수 있는 rebase
와 해당 소스코드를 누가 수정했는지 추적할 수 있는 blame
을 실습해 볼 수 있었고, 팀으로 간단하게 PR을 보내고 충돌을 해결하는 과정까지 실습해 볼 수 있었습니다.
그 외에도 commit message 작성 팁
과 미시적인 관점에서 보는 오픈소스 협업 개발과정
등 오픈소스에 대한 전반적인 배경지식들을 배울 수 있는 알찬시간이었습니다. 👍👍
OT & Study
Flutter Moum 프로젝트는 두 분의 멘토님과 12명의 멘티들이 6주간 함께 Flutter에 기여하는 방식으로 진행되었습니다. (예전에는 Flutter에 사용되는 Plugin을 오픈소스로 개발하는 활동을 했었는데, 이번 2020년 부터는 Flutter 자체에 기여하는 방식으로 변경되었습니다 🙊)
첫 2주 동안에는 Flutter 개발환경 세팅
, 기여 방법
및 Flutter 내부 동작원리
와 PR 올리는 방법
에 대한 스터디를 진행하였습니다.
활동 내역
전체 멘티들의 활동 내역은 Flutter Moum에서 보실 수 있습니다.
Flutter 프로젝트에 기여하는 방법은 이슈를 선정한 뒤 해결책을 PR(Pull Request)를 보내는 방식으로 진행되었고, 저는 4주간 총 5개의 PR을 진행하였습니다.
진행한 이슈 목록
1. [Closed] Add rowSpacing property to Table issue
✔︎ Issue : Padding inside Table #52248
✔︎ PR : [Table] adds rowSpacing property #63322
처음으로 진행했던 이슈는 Table
Widget의 TableRow
사이에 간격을 조절하는 rowSpacing
이라는 새로운 property를 추가하자라는 이슈였습니다.
먼저 rowSpacing
속성을 추가하기 위해 Table
Widget이 어떻게 구현되어있는지 분석하였는데, 첫 스터디 때 멘토님이 알려주신 Flutter 내부 동작원리
에 대한 이론이 매우 큰 도움이 되었습니다. 속성을 추가할 때 Wrap
Widget이 spacing
이라는 비슷한 속성을 가지고 있다는 것이 생각나서 이를 참고하여 rowSpacing
속성을 무사히 추가했습니다. 또한 테스트 코드도 spacing
을 테스트하는 방법을 참고하여 큰 이슈 없이 구현할 수 있었고, before & after 이미지를 추가하여 PR을 올렸습니다.
이틀 뒤 예전에 Column
과 Row
에 Spacing
속성을 추가하자는 이슈이 있었는데, 다른 대체할 방법이 많고 두 Widget은 복잡도가 너무 높은 상태라 거절당했던 사례와 비슷한 것 같다는 부정적인 코멘트가 달렸습니다. 이에 대해 반론을 열심히 준비하여 올렸지만, 다른 분도 비슷한 이유로 부정적인 코멘트를 달아주시면서 아쉽지만 첫 PR은 Closed
되었습니다.
진행하면서 배우고 느낀점
- 모든 이슈가 검증되어있고, 구현만 하고 해결만 한다면 merge 되는게 아님
- labeling이 달렸다고 검증된 이슈가 되는건 아님
- 처음으로 진행했던 PR인데, Wrap에
spacing
이라는 유사한 기능이 있다는 것을 알고 참고하여 생각보다 쉽게 진행할 수 있었음 - 아무 PR이나 다 받아주면 오픈소스가 엉망이 되어버리기 때문에 리뷰어들이 자신의 생각을 말하고 토론하는 문화가 신선했음
- 영어가 부족하여 토론에 자신이 없는점이 너무 아쉬움(영어 공부에 대한 동기부여가 됨)
- 쉬운 이슈보다는 Merger가 될 만한 의미있는 이슈를 잘 고르는게 더 중요한 것 같음
- Github 내에서 이슈 링크를 걸면 밑에 history가 남는다는 것을 알게됨
2. [Merged] ReorderableListView list items jump down on dragging issue
✔︎ Issue : ReorderableListView list items jump down on dragging #58411
✔︎ PR : [ReorderableListView] remove extra margin added after picking up the item #65080
두 번째로 ReorderableListView
에서 아이템을 들어올릴 때 선택한 아이템 하위의 항목들이 밑으로 밀리는 문제를 해결해보았습니다. 아래 gif
의 before
이미지를 보시면 C
를 들어올릴 때 C
하위에 여백이 생기면서 E ~ H
까지 항목들이 밑으로 살짝 밀리는 것을 보실 수 있습니다.
_dropAreaMargin
이 항목을 들어올릴 때 밑에 Margin
으로 추가되는 것이 원인이었고, 해당 값을 0으로 만들거나 삭제하여 이슈를 간단하게 해결할 수 있음을 확인하였습니다.
그런데 해당 값을 없애거나 0으로 만들면 _dropAreaMargin
값이 올바로 추가되었는지 확인하는 테스트 두 개(horizontal, vertical)가 Fail
되었고, 기존 테스트 코드가 깨지거나 변경되는 경우 Breaking Changes가 될 수 있기 때문에 질문을 올렸는데 10일 넘게 답변이 없었습니다.. 답답한 마음에 Discord에 질문을 올렸더니 Alex Li
님이 중요도가 P4로 상대적으로 낮은 이슈이니 일단 PR을 올리는게 빠를거라고 조언해주었습니다.
용기를 얻어 PR을 올릴 준비를 하였는데, _dropAreaMargin
을 삭제하는게 더 맞는 것 같았지만 Breaking Changes가 되지 않기 위해 최대한 조금만 변경해도 동일한 결과를 얻을 수 있는 수정을 선택하여 PR을 올렸습니다.
PR을 올린지 12일이 되던 날 reviewer가 assign 되었습니다. 7일 뒤 역시나 수정보다는 삭제하는 방법으로 이슈를 해결하는게 좋을 것 같다고 리뷰를 해주셨고, 추가로 Golden Test를 해달라고 하였습니다. 삭제하는 방법으로 고치는건 해결했지만, Golden Test라는 단어도 처음 들어봐서 당황했는데, 다행히 멘티님들 중에 Golden Test를 해보신 경험이 있어 이것 저것 조언을 얻을 수 있었습니다. 이후 동료 멘티님이 올린 PR도 참고하고 다른 Golden Test들도 참고하여 시행착오 끝에 커밋을 올릴 수 있었습니다.
Golden Test는 스크린샷을 찍어 이미지로 추출하여 UI 테스트를 하는 테스트 방법으로, 아래와 같이 테스트 코드상에서 이미지로 추출하고 싶은 Widget을 지정해 준 뒤 matchesGoldenFile()
을 실행하여 이미지로 추출할 수 있습니다.
1 | await expectLater( |
이후 --update-goldens
옵션을 넣어 flutter test --update-goldens test/foo/bar_test.dart
이렇게 테스트 코드를 실행해주면 Flutter 프로젝트 하위의 bin/cache/pkg/skia_goldens/packages/flutter/test/ 경로로 이미지가 추출됩니다.
아래 이미지는 ReorderableListView
에서 항목을 집어들었을 때 하위에 여백이 생기는지 테스트하기 위해 3개의 항목 중 두 번째 파란색을 집어든 상태의 Widget을 matchesGoldenFile()
로 실행하여 추출한 이미지입니다.
이렇게 추출된 이미지를 PR의 Description에 넣고 커밋을 올렸는데, Test 코드에 Key
를 const
로 선언하지 않아 lint error
가 발생했습니다. 커밋을 올리기 전에 flutter analyze --flutter-repo
로 검사를 했다면 로컬에서 미리 알 수 있었던 애러인데 깜빡해서 바쁜 리뷰어들의 시간을 빼앗았습니다. 🙏
애러를 고친 뒤 다시 커밋을 올리고 2020년 9월 12일 토요일 드디어 머리털나고 처음으로 말로만 듣던 LGTM을 영접하게 되었습니다. 🎉🎉🎉
진행하면서 배우고 느낀점
- triage를 하시는 분들은 리뷰어가 아니라는 사실을 깨달음
- 중요도가 상대적으로 낮은(P4이하)는 일단 PR을 올리는게 진행이 빠름
- 기존 코드나 테스트가 Fail 된다고 무조건
Breaking Changes
가 되는건 아님 - 오랫동안(1주일 이상) 답장이 없는 경우 Discord hackers 채널에서 핑을 보내면 신속한 답변을 받을 수 있음
- 이미지를 이용하여 테스트를 하는 Golden Test 를 경험해봄
- 기존에 작성되어있는 테스트 코드들이 하나 같이 보물들임. 어떤 테스트를 어떻게 해야하는지 기존 테스트를 보고 많이 배움
- 구글 개발자가 내 코드를 봐준다는게 정말 흔치 않은 기회인 것 같음
- 마지막
Golden Test
에 const를 빼놓고 올려서 lint error가 발생했는데, 로컬에서flutter analyze --flutter-repo
명령어를 실행했더니 동일한 애러가 발생함을 확인함. 꼭 PR을 올리기 전에 analyze를 돌려야겠다고 다짐함 - LGTM을 받으니 그 동안의 노력이 모두 보상받는 기분이 들음
3. [Merged] ReorderableListView dropping animation issue
✔︎ Issue : N/A
✔︎ PR : [ReorderableListView] Fix item dropping animation #64140
다음은 두 번째 이슈에 대한 PR을 작성하는 도중 ReorderableListView
에서 아이템을 내려놓을 때 애니메이션이 부자연스러운 것을 발견하여 별도의 Issue 없이 바로 PR을 준비하였습니다. 아래 이미지의 before
를 보시면 C
항목을 내려놓을 때 B
와 C
사이에 여백이 잠깐 생겼다 사라지는 것을 보실 수 있습니다.
분석결과 항목을 내려놓을 때 애니메이션의 시작 지점이 0이 아니라 0.1부터 시작하기 때문에 발생하는 것으로 파악하였고, 아주 간단한 수정이었기에 별도의 테스트 코드 없이 PR을 올렸습니다.
그런데 테스트는 미래에 여러 변화들이 현재 해결한 문제들을 재발하는건 아닌지 자동으로 검사해주는 기능이기 때문에 테스트 코드를 추가해달라고 요청해주셔서 항목을 내려놓을 때 위치를 테스트하는 코드를 추가하여 커밋을 올렸습니다. 이후 1주일이 넘도록 리뷰어의 응답이 없어서 Discord에 핑을 보냈는데, 알고보니 휴가 중이신 것을 확인하고 굉장히 죄송했습니다. 🙏
진행하면서 배우고 느낀점
- 핑을 보내기 전에 혹시 휴가가신건 아닌지 github 등에서 확인해보기
- 이슈가 없더라도 발견된 문제는 바로 PR을 보내도 괜찮다는 사실을 알게됨
- 매우 작은 변화도 항상 테스트를 작성해야함
- 대규모 프로젝트일수록 테스트 코드는 필수임을 느낌
4. [Closed] SwitchListTile.adaptive has splash effect on iOS & macOS issue
✔︎ Issue : SwitchListTile.adaptive has splash effect on iOS #35399
✔︎ PR : [SwitchListTile.adaptive] Remove splash effect on iOS and macOS #65081
네 번째로 진행한 이슈는 SwitchListTile.adaptive
Widget이 iOS와 macOS에서 Material
Widget인 InkWell
효과(splash effect)가 나타난다는 이슈입니다. iOS에는 당연히 InkWell
효과가 없기 때문에 올바른 정정이라고 생각하여 원인 분석을 시작했습니다. SwitchListTile
는 내부적으로 ListTile
Widget을 사용하고 있는데 해당 Widget 하위에 InkWell
이 들어있었습니다.
따라서 직접적으로 InkWell
을 예외처리할 순 없었고, adaptive
로 실행되고 플랫폼이 iOS 또는 macOS인 경우에만 Theme
의 splashColor
와 highlightColor
를 투명하게 만들어 문제를 해결하였습니다.
왼쪽 before를 보시면 iOS에 SwitchListTile.adaptive
를 클릭할 때 흰색 Splash effect가 있는데, after에서는 해당 항목을 선택할 때 별 다른 effect가 없는 것을 보실 수 있습니다.
이렇게 PR을 올리고 행복하게 LGTM을 받을 수 있을 줄 알았는데, 아니나 다를까 Reviewer로 LongCatIsLooong
님이 지정되면서 아래와 같이 SwitchListTile
은 Material
Widget이기 때문에 ListTile
와 같이 Splash effect가 있어야한다고 코멘트를 달아주었습니다.
코멘트를 받고 생각해보니, InkWell
이 ListTile
에 속해있고 이 위젯은 플랫폼에 따라 다르게 구현되어있지 않은데, 이를 사용하는 SwitchListTile
에서만 플랫폼에 따라 다르게 구현하는건 근본적으로 잘못된 해결방법인 것 같았습니다. 즉 ListTile.adaptive
또는 iOS의 전용 CupertinoListTile
과 같은 Widget들이 나와야 올바르게 문제를 해결 할 수 있을것 같다는 생각이 들었습니다.
실제 iOS에서는 UITableViewCell
이라 불리는 ListTile
과 같은 역할을 하는 컴포넌트가 별도로 존재하고 있고 이에 상응하는 Widget을 구현하자라는 논의도 #19228에서 진행되고 있었습니다. 일단 본 Issue는 부모 Widget과 연관되어 문제가 확대되었고, 이로인해 진행은 잠정 중단되었습니다.
진행하면서 배우고 느낀점
- 토론을 통해 본질적인 문제의 원인과 더 나은 해결방법을 찾아가는 과정을 경험함
- 1차원적인 해결책을 가지고 PR을 올리기 보다는 좀 더 깊이 생각해보고 토론한 뒤에 진행하는게 중요하다는 사실을 깨달음
- 이슈 또한 합리적인 의심을 통해 고민해봐야하는 대상이라는 사실을 알게됨
- Cat person이 많아서 괜시리 좋았음
5. [Merged] Tab indicator flies off issue
✔︎ Issue : Tab indicator flies off when tabs are tapped in quick succession #48000
✔︎ PR : [Tabs] Fix tab indicator flies off #65463
마지막으로 진행했던 이슈는, Flutter의 메인 Contributor중 한 명인 Hixie
님이 제보한 이슈입니다.
아래 before를 보면 Tab
Widget의 Indicator
가 1을 클릭한 뒤 2를 재빠르게 누르면 왼쪽 어딘가로 사라져버렸다가 되돌아오는 것을 볼 수 있습니다.
문제의 원인을 분석해보니 애니메이션이 작동중일 때 tab을 선택하는 경우 작동하는 코드가 잘못 구현되어 있었고, 또한 해당 기능을 대체할 수 있는 코드가 하위에 이미 존재하고 있었습니다. Hixie
님이 샘플 코드를 제공해주실 때 디버깅을 위해 timeDilation
설정하여 전체 애니메이션을 10배 느리게 작동하도록 되어있음에도 불구하고 indicator
의 속도가 매우 빠른 것을 볼 수 있는데 그 이유는 아래의 코드에 있습니다.
문제의 코드에서 _currentRect
를 Rect.lerp()
에 넣은 뒤 그 결과를 다시 _currentRect
로 설정하고 있는데 이런 경우 indicator
는 가속 운동을 하게됩니다.
1 | _currentRect = Rect.lerp(targetRect, _currentRect ?? targetRect, _indexChangeProgress(controller)); |
이해를 돕기 위해 DartPad로 샘플코드를 준비했습니다.
1 | import 'package:flutter/material.dart'; |
위에서 case 2
가 문제의 상황인데, 결과가 10씩 선형 증가하지 않고 급격히 증가되는 것을 보실 수 있습니다.
그 외에도 화면상에서 사라져버리는 이유를 규명했지만, 아래 이미지로 자세한 설명은 대체하도록 하겠습니다.
불필요한 코드를 삭제하고, 본 문제에 대해 정상적으로 작동하는지 확인할 수 있는 테스트 코드만 추가하여 PR을 올렸습니다.
그런데 해당 코드가 올바르게 작동하지 않는 예외 케이스를 발견하였고, 이를 해결하기 위해 리뷰어와 아이디어 논의를 진행하였습니다. 그러던 중 리뷰어님이 심플하면서 완벽한 알고리즘을 제시했고, 이를 구현하여 PR을 올렸고 드디어 Merged 되었습니다.
진행하면서 배우고 느낀점
- 멘토님도 예전에 Tab 관련해서 진행을 했었던 적이 있는데, 이 부분에 잘못 구현된 코드들이 많이 산재하고 있다고 들음. 직접 잘못 구현된 코드들을 보고나니 리뷰어들이 왜 그렇게 까탈스럽게 리뷰했는지 이해가 되고, 그들의 역할이 얼마나 중요한지 알게 됨
- DartPad로 코드 조각을 구현하여 공유하는 방법을 배움
- 지금까지 진행했던 이슈 중 가장 어려웠던 이슈로 알고리즘의 중요성을 느낌
후기
오픈소스 컨트리뷰션은 쉽지 않았습니다. 처음 개발 환경을 구축하는 것 부터 어려웠고 진입장벽으로 봐야하는 영어 문서는 굉장히 방대했고, 심사숙고 끝에 이슈를 선택하여 힘겹게 작성한 첫 번째 PR은 이슈와 함께 거절당했습니다. 이슈만 해결되면 Merge 될 줄 착각하고있었던 저는 쉬운 이슈만 골라서 접근했다가 그게 아니라는 것을 깨달았고 이후 Merge 가능성이 있는 이슈들을 나름 판단하여 PR을 보냈지만 그 사이에 6주가 훌쩍 지나가버렸습니다. 귓동냥으로만 들었던 LGTM은 이미 전설속의 단어처럼 느껴졌고, 활동기간이 끝나가는 시점에서 “나는 의미있는 활동을 한 것일까?” 생각이 들곤 했습니다.
그러나 오픈소스 활동을 하기 전과 후의 나를 비교해보면, Github와 Git에 대한 이해도가 현저히 높아졌고, 직접 Flutter 내부 소스를 열어보면서 내부 동작원리에 대해 이전보다 깊게 이해할 수 있게되었을 뿐만 아니라 테스트 코드를 작성해본 경험이 부족했었는데, 수두룩하게 쌓여있는 비슷한 사례의 테스트 코드들을 참고하면서 자연스럽게 테스트란 무엇이고 어떻게 하는지에 대한 모범 사례들을 배울 수 있었습니다. 또한 내 코드를 뛰어난 개발자들에게 내 코드를 리뷰받을 수 있다는 것 자체가 얼마나 흔치않은 기회인지 깨달으면서 정말 많은 것들을 배우고 받았다는 것을 알게 되었습니다.
만약 혼자서 오픈소스 생태계에 뛰어들었다면, 개발환경 구축단계에서 포기했을것 같습니다. 하지만 멘토님들의 족집게 같은 특강과 함께 삽질을 하며 노력한 동료 멘티님들이 계셨기 때문에 끝까지 포기하지 않고 완주할 수 있었습니다.
전 세계의 다양한 인종의 사람들이 Flutter라는 오픈소스에 대한 관심사로 모여 더 나은 Flutter를 위해 함께 토론하고 코드를 나누는 활동들이 굉장히 신선했지만 부족한 영어 실력으로 더 깊은 대화를 하지못했던 부분들이 너무나 아쉬웠습니다.
6주간 컨트리뷰션을 하면서 알게된 노하우를 4가지 정도를 공유하자면,
- 첫 번째로 이슈는 정말 아무나 자기 생각대로 올린 문제나 제안들이고, 이슈에 라벨링을 달아주는 사람들도 해당 이슈가 정말 필요한지 아닌지 깊게 고려하진 못한다는 점을 알아야합니다. 그렇게 때문에 이슈가 타당한지 아닌지에 대해선 스스로 판단하셔야 합니다.
- 두 번째는 작은 기능을 수정하거나 추가할 때라도 언제나 테스트 코드를 작성하는 것입니다. 처음에는 리뷰어가 테스트 코드를 추가해달라고 요청하면 해야지라고 생각을 했었는데, 거의 무조건 테스트 코드를 요청받게 됩니다. 또한 이렇게 되면 1주면 끝날 이슈가 2주로 늦어지게 되기 때문에 처음부터 바로 테스트 코드를 작성하시는 것을 추천드립니다.
- 세 번째 팁은 리뷰어가 1주일이 지나도록 이슈 검토를 안할 경우 Discord에 hackers 채널에서 해당 리뷰어에게 핑을 보내 리뷰를 요청할 수 있습니다. 단, 핑을 보내기 전에 해당 리뷰어가 휴가인지 아닌지 github 계정을 들어가서 확인해보시기 바랍니다.
- 네 번째 팁은 구현하려는 기능과 비슷한 기능의 코드들이 이미 코드상에 많이 있기 때문에 이 코드들을 참고해서 구현하면 매우 수월하게 진행할 수 있습니다. 특히 테스트 코드를 작성할 때 큰 도움이 되었습니다. (오픈소스는 사실 보물상자 💎)
끝으로 오픈소스를 아직 경험해보지 못하신 분이 계시다면 꼭 경험해보시길 강력 추천드립니다 👍👍