📝

GitHub로 협업하기

1. Branch 전략 수립하기

1.1. Branch 전략이란

Branch 전략이란 프로젝트 진행 시 효율적인 버전 관리를 위해 사용하는 일종의 워크플로우(work-flow)입니다.
프로젝트를 진행하며 기능 추가, 기능 테스트 등을 진행하게 됩니다. 이 때 예기치 못한 버그가 발생할 수 있습니다. 우리는 버그를 찾아 수정하고 최종적으로는 완성된 프로젝트를 배포하게 될 것입니다.
이렇게 프로젝트를 진행해 나갈 때에 목적에 맞는 브랜치 사용, 유지보수를 위한 브랜치 효율 관리, 롤백을 위한 전략 등 프로젝트를 효율적으로 관리하기 위해 고안된 것이 Branch 전략입니다.

1.2. Branch 전략의 종류

효율적인 프로젝트 진행을 위해 사용하는 Branch 전략에는 대표적으로 어떤 것들이 있는지 알아보겠습니다.
  • Git-flow(main, hotfix, develop, feature, release)
  • GitHub-flow(main, branch)
  • Feature branch(main, develop, feature)

1.2.1. Git-flow

Git-flow는 대표적인 branch 전략 중 하나로 대규모 프로젝트에서 사용할 때 큰 장점을 보입니다. feature, develop, release, main(master), hotfix 브랜치로 이루어진 전략입니다.
 
알잘딱깔센 Github 핵심 개념알잘딱깔센 Github 핵심 개념
알잘딱깔센 Github 핵심 개념
  • feature feature 브랜치는 develop 브랜치에서 생성되고 기능을 구현할 때 생성하는 기능 단위의 브랜치입니다. 생성한 브랜치에서 기능 구현을 완료하였을 경우 develop 브랜치로 기능을 더해 나갑니다. 일반적으로 feature/기능과 같은 이름을 붙입니다.
    • feature/add 브랜치를 생성하고 아래와 같이 2개의 수를 더해주는 코드를 작성해봅시다. 이렇게 한 기능을 구현한 뒤, develop 브랜치에 기능을 더해 나갑니다. 예를 들면, 빼기나 곱하기, 나누기 등의 기능을 추가할 때마다 push 를 하는 거죠.
      //calculator.js function add(a,b){ return a+b; }
 
  • develop develop 브랜치는 feature 브랜치에서 구현된 기능이 모여 브랜치들의 기둥 역할을 하는 브랜치입니다. 일반적으로 develop과 같은 이름을 붙입니다.
    • //calculator.js function add(a,b){ return a+b; } function sub(a,b){ return a-b; } function mul(a,b){ return a*b; } function div(a,b){ return a/b; }
      협업을 진행하는 팀원들이 사칙연산의 각 기능들을 구현하고 develop 브랜치로 구현한 기능들을 더하면, 위의 코드처럼 합쳐집니다.
       
  • release release 브랜치는 develop 브랜치에서 생성되며, 실제 배포 전 테스트를 하거나 버그 확인을 하기 위한 브랜치입니다. develop 브랜치에서 계획했던 기능의 구현이 완료되었을 때, 기능 테스트 및 버그 확인을 위해 생성합니다. 일반적으로 release-1과 같은 이름을 붙입니다.
    • //calculator.js function add(a,b){ if(typeof a !== "number" || typeof b !== "number"){ return "숫자만 입력해주세요." } return a+b; } function sub(a,b){...} function mul(a,b){...} function div(a,b){...}
      위와 같이 사칙연산의 기능을 가진 서비스를 만들었습니다. release 브랜치에서 테스트하던 중 숫자를 입력하지 않았을 경우에도 계산기가 수행이 되는 버그를 확인하게 되었다고 가정해 봅시다. 이 경우, 기능을 수정한 후에 develop 브랜치로 merge 하고 다시 기존의 release 브랜치에서 배포 전 테스트 및 버그 확인을 진행합니다.
       
  • main(master) main 브랜치는 release 브랜치에서 생성되며 실제 배포되는 소스의 브랜치입니다.
 
  • hotfix hotfix는 main 브랜치를 통해 배포가 진행되었는데 문제가 발생한 경우 생성하는 브랜치입니다. main 브랜치를 통해 생성되며 문제가 해결된 이후 main 브랜치와 develop 브랜치로 merge 됩니다. 일반적으로 hotfix-1과 같은 이름을 붙입니다.
 

1.2.2. GitHub-flow

앞서 알아본 Git-flow는 대규모 프로젝트에서 사용할 경우 관리가 용이하다는 장점이 있습니다. 하지만 그만큼 많은 브랜치를 관리해야 한다는 복잡함이 존재하죠. Github-flow는 이런 Git-flow의 단점을 해결할 수 있는 branch 전략입니다. Github-flow는 main(master), (branch) 브랜치로 이루어져 있습니다. Github-flow 전략은 main 브랜치를 통해 모든 브랜치가 생성되고 생성된 브랜치 이름을 통하여 기능 구현이나 버그 수정 등의 생성 목적을 명시합니다.
알잘딱깔센 Github 핵심 개념알잘딱깔센 Github 핵심 개념
알잘딱깔센 Github 핵심 개념
  • main(master) main 브랜치는 배포를 위한 브랜치이며, 전략의 중심이 되는 브랜치입니다.
 
  • (branch) (branch)는 main 브랜치를 통해 생성됩니다. 기존 Git-flow와는 다르게 명시적인 브랜치의 역할이 없는 main(master) 브랜치 이외의 브랜치입니다. 기능 구현(feature)이나 버그 해결(hotfix) 등의 브랜치 목적을 이름에 명시해주어야 합니다.
이 외에도 gitlab-flow를 비롯한 다양한 전략이 있고 전략에는 정답이 없습니다. 따라서 각 브랜치들의 전략을 잘 이해하고 현재 진행하는 프로젝트의 상황에 따라 효율적인 브랜치 전략을 수립하는 것이 중요합니다.

2. GitHub Projects를 사용하여 프로젝트 관리하기

효율적인 브랜치 전략을 수립했다면 이제 프로젝트를 진행하면 됩니다. 이 때, 일의 진행이 어떻게 되어가는지 팀원 모두가 볼 수 있도록 정리되면 좋겠죠? 더불어 어떤 이슈가 발생했고 누가 어떻게 해결했는지도 확인할 수 있다면 좋을 거에요. GitHub Projects의 board 기능을 활용하면 팀 프로젝트의 계획을 세워 공유하고 일의 진행 단계를 한 눈에 볼 수 있어서 협업 시 큰 도움이 됩니다. 여기서 board는 템플릿을 말하는데요, GitHub Projects board에는 ‘None, Basic kanban, Automated kanvan, Automated kanban with reviews, Bug triage’ 5가지 종류가 있습니다. 이 중에서 Basic kanban 템플릿을 활용하여 프로젝트를 관리하는 방법에 대해 알아봅시다.
💡
kanban 템플릿은 칸반보드라 불리는데, 여기서 칸반은 일본어로 ‘간판’이라는 뜻입니다. ‘눈에 보이는 신호’라는 의미로 붙여졌어요. 칸반보드는 업무 진행 단계를 보여주는 열로 구성된 가상의 보드판이며, 프로젝트를 시각적으로 관리하기에 유용한 개발 방법론의 도구입니다. 실무에서 많이 사용하는 프로젝트 관리 툴인 트렐로(trello), 노션(notion), 지라(jira)에서도 이 칸반보드를 지원하고 있죠. 그러니 현업에서 많이 활용되고 있는 칸반보드의 형태를 눈에 잘 익혀두세요!
notion에서 지원하는 칸반보드notion에서 지원하는 칸반보드
notion에서 지원하는 칸반보드
 
1) 우선, 상단의 Projects 페이지로 들어가 New project 를 생성하고 템플릿 목록에서 Basic Kanban 을 선택합니다.
New project 생성New project 생성
New project 생성
Basic kanban 선택Basic kanban 선택
Basic kanban 선택
 
2) 프로젝트의 이름과 설명을 작성한 후 프로젝트를 생성합니다. 프로젝트가 생성되면 아래와 같이 To do / In progress / Done 세 종류로 구성된 칸반 보드가 등장합니다. + 버튼을 클릭하여 텍스트를 추가해 나가면 됩니다! 간단하죠?
notion imagenotion image
notion imagenotion image
 
할일 목록을 드래그 해서 직접 옮겨도 되지만, Automated kanvan 템플릿을 활용하면 더욱 편리합니다. 이 Automated kanvan 기능을 사용하면 이슈의 상태가 변화할 때마다 자동으로 To doDone 으로 옮겨줍니다. 협업 용도에 따라 칸반보드 외에 다양한 템플릿을 사용해서 프로젝트를 관리해보세요.
우측 사이드 바에는 issue 카드를 생성할 수 있는데, 지금부터 이 이슈를 생성하고 활용하는 방법에 관하여 자세히 알아보겠습니다.

3. GitHub Issues 생성

3.1. GtiHub Issues란?

GitHub Issues란 GitHub에서 작업하는 동안 프로젝트의 다양한 이슈들을 관리해 주는 기능입니다.
이슈는 버그와 개선사항 뿐만 아니라 새롭게 추가되는 기능 등을 포함하며 등록된 이슈를 기반으로 작업을 진행합니다. 또한 GitHub Issues는 등록된 이슈를 추적하고 구성원들과 진행 상황을 공유하며 작업을 관리하는 기능을 제공합니다. 오픈 소스 레파지토리에 직접 Issue를 생성하여 아이디어 기여도 가능합니다.
 

3.2. 이슈 생성(open)하기

3.2.1. 이슈 생성 방법

  1. 레파지토리 탭 메뉴의 Issues를 클릭 후 우측에 있는 New issue 버튼을 클릭합니다.
    1. notion imagenotion image
 
  1. 이슈 제목과 내용을 작성 후 Submit new issue 버튼을 클릭하여 생성합니다. 우측의 Assigness, Labels 등을 사용하여 더 다양한 협업이 가능합니다. 자세한 내용은 아래 ‘3. 이슈 활용하기’에서 다루겠습니다.
    1. notion imagenotion image
 
  1. 생성된 이슈에 코멘트(comment)를 남길 수 있습니다.
    1. notion imagenotion image
 

3.2.2. 유용한 마크다운(md) 문법

이슈 내용은 마크다운 문법에 맞춰 작성하며 Preview 탭을 눌러 미리보기가 가능합니다.
Write 탭 화면Write 탭 화면
Write 탭 화면
Preview 탭 화면Preview 탭 화면
Preview 탭 화면
 
  1. 권한을 부여한 작업자 태그 : @작업자id 로 직접 태그가 가능합니다.
  1. 커밋 링크 : 커밋 URL을 복사하여 붙여넣으면 자동으로 커밋 id와 링크로 표시됩니다.
  1. 파일 코드 미리보기 링크 (이슈와 같은 레파지토리의 파일이어야 합니다.)
      • 미리보기 하고자 하는 파일로 들어가 원하는 줄을 클릭합니다.
      • 다중 선택의 경우 시작 줄을 클릭 후 Shift 키를 누른 상태로 마지막 줄을 클릭합니다.
      • 좌측에 생성된 메뉴 버튼(···) → Copy permalink를 클릭하여 복사된 링크를 붙여넣기 합니다.
        • notion imagenotion image
 
  1. 다른 이슈나 코멘트 링크 (이슈와 같은 레파지토리의 파일이어야 합니다.)
      • #이슈넘버 를 직접 타이핑하거나 이슈 URL을 복사하여 붙여넣으면 자동으로 표시됩니다.
      • 코멘트 링크를 복사하여 붙여넣으면 자동으로 표시됩니다.
Write 탭 화면Write 탭 화면
Write 탭 화면
Preview 탭 화면(상단의 번호와 비교하면서 보세요.)Preview 탭 화면(상단의 번호와 비교하면서 보세요.)
Preview 탭 화면(상단의 번호와 비교하면서 보세요.)
 

3.2.3. 이슈 템플릿 활용하기

하지만 이슈를 생성할 때 마다 기본 내용을 채우는 것은 번거로운 일입니다. GitHub에서는 이러한 번거로운 수고를 줄이기 위해 이슈 템플릿 기능을 지원합니다.
 
  1. 이슈 템플릿을 만들기 위해 우선 Settings 탭으로 이동 후 스크롤을 내려 FeaturesIssuesSet up templates 버튼을 클릭해줍니다.
    1. notion imagenotion image
      notion imagenotion image
 
  1. Add template을 누르면 아래와 같이 3개의 template을 기본으로 제공하고 있습니다. 첫 번째는 Bug 관련(Bug report), 두 번째는 기능 관련(Feature request), 세 번째는 사용자 정의 템플릿(Custom template)입니다. 원하는 템플릿을 선택합니다.
    1. notion imagenotion image
 
  1. 선택하시면 기본 템플릿이 생성되며, 수정 및 상세 내역을 보고 싶으신 경우 Preview and edit버튼을 클릭합니다.
    1. notion imagenotion image
 
  1. 내용 수정을 원하는 경우 제목 옆의 수정 버튼(🖋)을 눌러 내용을 수정한 후 Propose changes를 클릭합니다.
    1. 수정 버튼 누르기수정 버튼 누르기
      수정 버튼 누르기
      수정 내용 적용하기수정 내용 적용하기
      수정 내용 적용하기
 
  1. 이후 생성된 템플릿을 커밋합니다.
    1. 수정한 Template Commit수정한 Template Commit
      수정한 Template Commit
 
  1. GitHub repository에 ISSUE_TEMPLATE이 생성되고 새로운 이슈 생성 시 Get started 버튼을 눌러 미리 만들어둔 Template을 사용할 수 있습니다.
    1. ISSUE_TEMPLATE 추가된 모습ISSUE_TEMPLATE 추가된 모습
      ISSUE_TEMPLATE 추가된 모습
      ISSUE_TEMPLATE 사용ISSUE_TEMPLATE 사용
      ISSUE_TEMPLATE 사용
 

3.3. 이슈 활용하기

GitHub 이슈는 Assignees, Labels 등을 설정할 수 있습니다. 이런 설정들을 활용해 원하는 이슈만 정렬하여 보거나, 이슈를 할당하고 진행도를 파악하여 보다 효율적인 협업을 가능하게 합니다.
notion imagenotion image
 

3.3.1. Assignees

이슈 담당자를 할당합니다. 담당자가 없는 상태로 이슈 생성이 가능하며, 생성 후 추가 및 수정도 가능합니다.
  • 본인에게 할당할 경우 : assign yourself를 클릭
  • 여러 사용자에게 할당할 경우 : 우측의 톱니바퀴(⚙️)를 클릭하여 리스트에 표시된 사용자들 클릭
본인에게 할당할 경우본인에게 할당할 경우
본인에게 할당할 경우
여러 사용자에게 할당할 경우여러 사용자에게 할당할 경우
여러 사용자에게 할당할 경우
 

3.3.2. Label

카테고리처럼 성격에 맞게 이슈를 구분하기 위해 사용합니다. 우측의 톱니바퀴(⚙️)를 클릭하여 리스트에 표시된 항목을 선택합니다.
notion imagenotion image
  • Custom Label 생성하기
      1. 이슈 탭에서 Labels를 클릭합니다.
        1. notion imagenotion image
      1. New label을 클릭하여 Label name, Description(선택), Color 값을 입력하고 Create label을 클릭하여 생성합니다.
        1. notion imagenotion image
 

3.3.3. Projects

레파지토리에 생성된 Project에 이슈를 추가할 경우 사용합니다. Projects의 자세한 내용은 위의 2. GitHub Projects를 사용하여 프로젝트 관리하기에서 확인 가능합니다.
  1. 우측의 톱니바퀴(⚙️)를 클릭합니다.
  1. 리스트에 표시된 항목 중 원하는 프로젝트를 선택합니다.
      • Recent : 최근에 사용한 project
      • Repository : 레파지토리에 생성된 project
      • User : 사용자의 project
        • notion imagenotion image
 

3.3.4. Milestones

Milestone이란 프로젝트 성공을 위해 반드시 거쳐야 하는 중요한 지점을 뜻 합니다. 정리하자면 프로젝트의 주 단위 목표 혹은 주요 기능 단위에 대한 여러 개의 Milestone들을 만들 수 있습니다.
GitHub의 Milestones는 Due Date를 설정하고 해당 기간 동안 수행할 Issue들을 등록해 진행 상황을 확인할 수 있는 기능을 제공합니다. 개발 목적에 따라 Milestone으로 만들고, 관련 이슈들을 생성한 후, Milestone에 등록한 이슈들을 추적하여 진행 상황을 Progress bar로 보여줍니다.
우측의 톱니바퀴(⚙️)를 클릭하여 리스트에 표시된 항목 중 하나를 선택합니다.
notion imagenotion image
  • Milestone 생성하기
      1. 이슈 탭에서 Milestones를 클릭하고 이동한 페이지에서 New milestone을 클릭합니다.
        1. notion imagenotion image
          notion imagenotion image
       
      1. Title, Due date(선택), description(선택)을 입력하고 Create milestone을 클릭하여 생성합니다.
        1. notion imagenotion image
       
      1. 완료하면 아래와 같이 표시됩니다.
      notion imagenotion image
 

3.3.5. 커밋과 Issue 연결

커밋 메시지 입력 시 이슈 번호를 입력하게 되면 이슈 페이지에 자동으로 연결되어 표시됩니다. 이를 잘 활용하면 이슈별 커밋 기록을 한 눈에 파악할 수 있어 편리합니다.
 
notion imagenotion image
 
notion imagenotion image

3.4. 이슈 종료(close)하기, 재오픈(reopen)하기

3.4.1. 이슈 직접 종료하기

해결되지 않은 이슈들에 집중하기 위해 해결된 이슈들은 close issue 버튼(아래 사진의 1번)을 눌러 이슈를 닫아줍니다. close된 이슈들은 사라지지 않고 close tab에 기록으로 남습니다. 만약 이슈 기록을 지우고 싶다면 이슈 페이지 하단의 delete issue 버튼(아래 사진의 2번)을 눌러 삭제할 수 있습니다.
notion imagenotion image
 

3.4.2. 이슈 재오픈하기

이슈가 제대로 해결이 되지 않은 상태로 close된 경우 새로운 이슈를 생성하는 것이 아닌 기존의 이슈를 reopen해주면 됩니다. Issues > Closed 탭으로 이동하여 재오픈 할 이슈로 들어간 후 다시 해결해야 될 이슈를 코멘트로 남겨줍니다. 그리고 Reopen 버튼을 눌러 이슈를 재오픈 해줍니다.
notion imagenotion image
 

3.4.3. 커밋 메시지로 이슈 종료하기

커밋 메시지로 이슈를 close 할 수 있는 키워드들이 있습니다. 이슈에 대한 기능을 수정 완료한 후 Commit을 할 때 아래 키위드들 중 하나의 키워드를 같이 첨부하면 이슈 탭에 들어가지 않고도 이슈를 close할 수 있습니다.
 
💡
이슈를 Close할 수 있는 키워드 close, closes, closed, fix, fixes, fixed, resolve, revolves, resolved
$ git commit -m "커밋 메시지 - close #[이슈 번호]" $ git commit -m "커밋 메시지 - fix #[이슈 번호]" $ git commit -m "커밋 메시지 - resolve #[이슈 번호]"
notion imagenotion image

4. Git pull 전략짜기

협업 시 git history 는 매우 중요한 요소입니다. 이 때문에 위에서 배운 브랜치 전략에 따라, 팀에 어떤 pull 옵션이 적합한지 규칙을 잘 세우는 것이 권장됩니다. 팀 프로젝트를 진행 하다보면 pull 했을 때 간혹 다음과 같은 경고 문구가 나타나는데요, 이는 pull에 대한 옵션 없이 명령하는 것이 권장되지 않기 때문입니다.
warning: Pulling without specifying how to reconcile divergent branches is discouraged. You can squelch this message by running one of the following commands sometime before your next pull: git config pull.rebase false # merge (the default strategy) git config pull.rebase true # rebase git config pull.ff only # fast-forward only You can replace "git config" with "git config --global" to set a default preference for all repositories. You can also pass --rebase, --no-rebase, or --ff-only on the command line to override the configured default per invocation.
그렇다면 git pull의 옵션에는 어떤 것들이 있는지 세 가지 방법에 대해 알아보겠습니다.

4.1. git config pull.rebase false

pull을 받을 때 rebase를 하지 않고(false) 기존에 우리가 알고 있던 merge로 진행하겠다고 설정합니다. 하지만 이렇게 git pull을 수행하면 불필요한 git merge commit을 생성하게 되니 참고하세요.
notion imagenotion image
 

4.2. git config pull.rebase true

git pull을 받을 때 rebase로 pull을 진행하겠다는 의미인데요, 여기서 rebase는 re+base입니다. 한마디로 커밋 history의 base를 다시 정의해서 커밋 이력을 재정렬하겠다는 것이죠.
notion imagenotion image
notion imagenotion image
 
만약 팀의 git-flow 규칙을 rebase로 pull을 진행하기로 결정했다면, 처음부터 아래 명령어를 입력하면 됩니다. 다음 명령 이후의 모든 브랜치에서 git pull 시에 기본적으로 rebase가 설정됩니다. 이 방법은 commit 기록을 한줄로 깔끔하게 관리할 수 있습니다.
$ git config --global branch.autosetuprebase always
 
💡
git rebase를 제대로 알고 사용하지 않으면? git config pull.rebase true 옵션 후 git pull을 쓴다면, main 브랜치에서 feature 브랜치의 commit과 병합되고 feature 브랜치가 삭제되며 커밋 히스토리 자체를 변경 합니다. 이러한 변경사항은 협업 상황에서 커밋 히스토리의 불일치로 Git 꼬임을 발생시킬 수 있습니다. git rebase는 깔끔하다는 장점이 있지만 제대로 알고 쓰기 어렵고, 목적을 명확히 하지않고 사용하면 위험하기 때문에 사용 시 주의가 필요합니다! 아래 git rebase를 읽고 활용하세요.
 

4.2.1 git rebase

개인 작업 시 rebase를 적용하는 방법을 통해 rebase가 무엇인지 더 알아보겠습니다. 여기서 새로운 기능 구현을 위해 feature라는 브랜치를 분기하면, main 브랜치와 feature 브랜치가 공통으로 가지는 커밋 이력(base)가 생깁니다. 목표한 기능을 다 구현해서 main에 rebase로 합치는 상황을 가정해보겠습니다.
notion imagenotion image
 
  1. main 브랜치에서 feature 브랜치를 생성하고 기능을 구현합니다.
  1. main 기준으로 rebase를 실행하기 위해 브랜치를 이동합니다.
$ git checkout feature
 
  1. main 기준으로 rebase를 실행합니다. 이때, main 기준 feature 브랜치의 git history 가 정리됩니다.
$(feature) git rebase main First, rewinding head to replay your work on top of it... Applying: added staged command
💡
rebase로 종료된게 아니었나요? 지금까지 작업은 feature 브랜치에서 git history를 정리한 것입니다. main을 기준으로 정리된 git history를 반영해줘야하기 때문에 다음 명령어들을 지켜줘야 합니다!
 
  1. main으로 이동합니다.
$ git checkout main
 
  1. 지금까지의 git history를 main 브랜치에 반영되도록 해줍니다.
$(main) git merge feature
 

4.3. git pull -ff -only

git pull -ff -only 옵션을 사용하면 git config pull.rebase false에서 언급했던 git merge commit이 생기는 것을 방지할 수 있습니다. 단, fast-forwad(ff) 관계일 때만 pull을 허용합니다. 여기서 fast-forwad(ff)는 어떤 관계를 의미할까요?
예를들어, 두 개의 커밋 A 와 B 가 존재할 때, B 에 A 의 히스토리가 전부 포함되는 경우 커밋 A 는 커밋 B 에 fast-forward 한다라고 말합니다. 아래의 이미지를 통해 확인해보세요!
notion imagenotion image
notion imagenotion image
 
git merge commit이 생기는 것을 방지하는 옵션은 다음과 같습니다.
$ git pull —ff —only
 
매번 입력해야하는 번거로움 없이 다음 명령어를 사용하면 기본값으로 설정할 수 있습니다.
$ git config --global pull.ff only

4.4 git config 설정 초기화

git pull 옵션의 global 설정을 바꾸고 싶은 경우가 생길겁니다. 그럴 경우 git config를 확인하고 설정한 것을 취소하면 됩니다.
자신의 현재 git 설정을 다음 명령어로 확인할 수 있습니다.
$ git config --list
 
아래의 경우 pull.ff=only 로 pull 옵션이 설정되어있는 것을 확인할 수 있습니다.
notion imagenotion image
 
다음 명령어로 설정 중 지우고 싶은 항목을 초기화할 수 있습니다. 이때 [] 안에는 = 왼쪽의 값을 입력하면 됩니다.
git config --global --unset [지울 항목]
다음과 같이 입력 후 config 설정을 다시 조회해보면 pull.ff 설정이 사라진 것을 확인할 수 있습니다.
notion imagenotion image
notion imagenotion image