SSD Nodes Learn
가이드 Matt Connor작성자 Matt Connor · 업데이트됨 2026-07-24

Ubuntu 24.04 Ansible 설치 및 첫 Playbook 작성법

Ubuntu 24.04에서 pipx로 Ansible을 설치하고 VPS 보안을 강화하는 Playbook을 작성합니다. Permission denied 및 sudo 관련 오류 해결 방법과 inventory 설정법을 포함하여 초보자도 쉽게 서버를 자동화할 수 있습니다.

구축 목표

Ansible이 설치된 제어용 머신 1대와, 기본 이미지만 설치된 신규 Ubuntu 24.04 VPS 1대 이상이 필요합니다. 과정을 마치면 서버 목록이 포함된 inventory 파일, 인증 성공 여부를 확인하는 ad-hoc ping, 그리고 신규 VPS 설정 과정을 코드로 구현한 playbook을 갖게 됩니다. 이 playbook은 SSH key가 등록된 deploy user 생성, sshd 보안 강화, fail2ban 설치, unattended upgrades 설정, 그리고 모든 접속을 차단하기 전 OpenSSH를 허용하는 firewall 설정을 수행합니다. 서버가 1대든 20대든 동일하게 적용할 수 있습니다. 두 번 실행해도 두 번째 실행에서는 변경 사항이 발생하지 않아야 하며, 이것이 이 방식의 핵심입니다.

15년 동안 VPS를 프로비저닝하며 경험한 사실은 다음과 같습니다. 대부분의 사용자가 처음 5대의 서버는 수동으로 설정하지만, 6번째 서버를 설정할 때는 이전 설정 내용을 기억하지 못해 주말을 허비합니다. 이 가이드는 여러 Linux 서버 관리에 대한 내용을 심화하여 다룹니다. 세 개의 터미널에 동일한 apt install를 반복해서 입력하고 있다는 사실을 깨닫는 즉시 이 가이드를 확인하십시오.

Ansible의 정의

Ansible은 agentless 방식입니다. 관리 대상 서버에 별도의 daemon을 설치할 필요가 없습니다. control machine은 일반적인 SSH를 통해 연결됩니다. 그 후 대상 서버로 작은 Python module을 복사하여 실행하고, 출력된 JSON을 읽은 뒤 해당 module을 삭제합니다. 대상 서버에 필요한 유일한 조건은 python3이며, 이는 모든 순정 Ubuntu image에 이미 포함되어 있습니다. 핵심 개념은 idempotent입니다. 이는 작업이 동작이 아닌 state를 기술함을 의미합니다. 패키지에 대한 state: present는 "설치 프로그램을 실행하라"가 아니라 "이 패키지가 설치된 상태를 보장하라"는 뜻입니다. 이미 해당 state가 유지되고 있다면, Ansible은 아무 작업도 수행하지 않으며 changed 대신 ok을 보고합니다. 이 특성이 Ansible의 핵심입니다. 이 특성 덕분에 playbook을 다시 실행해도 안전하며, 이러한 안전한 재실행 기능이 shell script를 infrastructure로 변화시킵니다.

Prerequisites, and the gotchas up front

  • 제어용 머신: 노트북 또는 소형 VPS. Ubuntu 24.04를 기준으로 설명합니다. macOS는 Homebrew로 pipx를 설치하면 동일하게 작동합니다.
  • 대상 VPS: KVM 기반의 Ubuntu 24.04가 1대 이상 필요하며, root 계정으로 접속 가능해야 합니다. 대상 서버에는 별도의 소프트웨어를 설치하지 않습니다.
  • 모든 대상에 대한 SSH key 인증: Ansible은 ssh 명령의 인증 방식과 동일하게 작동합니다. 만약 ssh root@host 실행 시 비밀번호를 요구하면 Ansible은 실패합니다.
  • Ubuntu 24.04의 경우, pip install ansibleerror: externally-managed-environment 오류와 함께 종료됩니다. 이는 시스템 오류가 아닌 배포판의 의도된 정책입니다. pipx를 사용하십시오.
  • YAML의 공백은 구문(syntax)입니다. 들여쓰기가 잘못되면 mapping values are not allowed in this context 오류가 발생하며, 탭(tab) 문자가 포함되면 실행이 불가능합니다.
  • Playbook이 sshd를 강화하는 동안 각 대상 서버에 SSH 세션을 열어두십시오. 고객의 접속 차단 문제를 해결할 때, "깨끗한 상태에서 테스트하기 위해" 기존 세션을 닫아버린 경우가 많았습니다.

Step 1: pip가 아닌 pipx를 사용하여 control machine에 Ansible를 설치합니다

일반적으로 pip3 install ansible을 시도합니다. 하지만 설치 단계 초기에 Command 'pip3' not found, but can be installed with: sudo apt install python3-pip 오류가 발생하는 완전히 새로운 24.04 이미지에서는 pip 설치가 다음과 같은 문제로 이어집니다:

pip3 install ansible
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

Ubuntu 24.04는 시스템 Python을 externally managed (PEP 668) 상태로 지정합니다. 따라서 pip가 동일한 파일에 대해 apt와 충돌할 수 없습니다. --break-system-packages를 사용하지 마십시오. 해당 flag의 이름은 그 의미를 명확히 나타냅니다. 올바른 해결책은 pipx를 사용하는 것입니다. pipx는 Ansible 전용 isolated virtualenv를 생성하고 바이너리를 PATH에 추가합니다:

sudo apt update && sudo apt install -y pipx
pipx ensurepath
pipx install --include-deps ansible

PATH 변경 사항을 적용하려면 pipx ensurepath 실행 후 새 shell을 엽니다. --include-deps는 단순한 장식이 아닙니다. ansible 패키지는 자체적인 console scripts를 포함하지 않습니다. ansible, ansible-playbook 및 기타 항목들은 ansible-core 의존성의 entry points입니다. 따라서 flag를 사용하지 않으면 pipx는 No apps associated with package ansible or its dependencies 오류와 함께 설치를 거부합니다. ansible-core 대신 ansible 패키지를 설치하십시오. 전체 패키지에는 community collections이 포함되어 있습니다. 이 playbook은 그중 두 가지(ansible.posixcommunity.general)의 모듈을 사용합니다.

ansible --version

정상적인 결과는 ansible [core 2.19.x]와 같은 라인으로 시작하며 실행 중인 Python을 표시합니다. 현재의 모든 core release는 이 작업에 적합합니다. ansible: command not found~/.local/bin이 아직 PATH에 없음을 의미합니다. 새 shell을 열거나 source ~/.bashrc을 실행하십시오.

이것으로 설치가 완료됩니다. 대상(targets)에는 아무것도 설치되지 않습니다.

Step 2: 모든 대상에 대한 SSH key access

ssh-keygen -t ed25519 -C "ansible control"
ssh-copy-id root@10.0.0.10
ssh-copy-id root@10.0.0.20

그 다음, 각 호스트에 대해 다음을 실행하여 확인합니다:

ssh root@10.0.0.10 true && echo ok

이 한 줄의 명령은 두 가지 역할을 수행합니다. 첫째, 비밀번호 없이 키 인증이 작동하는지 확인합니다. 둘째, known_hosts에 host key를 기록합니다. 지금 바로 실행하십시오. Ansible은 기록되지 않은 host key가 있을 경우 실행 중간에 대화형 프롬프트를 표시하며, 이는 시스템이 멈춘 것처럼 보일 수 있습니다.

Step 3: 인벤토리 — 초기에는 INI를 사용하고, 규모가 커지면 YAML을 사용하십시오

인벤토리는 Ansible이 관리할 머신 목록을 담은 텍스트 파일입니다. 새 프로젝트 디렉토리에 inventory.ini 파일을 생성하십시오:

[vps]
web1 ansible_host=10.0.0.10
web2 ansible_host=10.0.0.20

[vps:vars]
ansible_user=root

web1은 사용자가 지정한 별칭입니다. 이 이름은 출력 결과에 표시되며 --limit web1의 대상이 됩니다. ansible_host는 실제 주소입니다. [vps]은 그룹이며, [vps:vars]는 해당 그룹 내 모든 호스트에 대한 변수를 설정합니다. ansible_user는 Ansible이 로그인할 계정입니다. 그 옆에는 -i을 반복해서 입력하지 않도록 ansible.cfg을 작성합니다:

[defaults]
inventory = inventory.ini

Ansible은 현재 디렉토리에서 ansible.cfg을 읽습니다. YAML 형식의 동일한 인벤토리 — inventory.yml으로 저장한 뒤 ansible.cfg이 해당 이름을 가리키도록 설정 — 는 호스트당 여러 변수를 관리해야 할 때 유용합니다:

vps:
  hosts:
    web1:
      ansible_host: 10.0.0.10
    web2:
      ansible_host: 10.0.0.20
  vars:
    ansible_user: root

두 형식은 동일한 역할을 수행합니다. 서버가 2대일 때는 INI 형식이 눈으로 확인하기 쉽습니다. 서버가 20대일 때는 YAML 형식이 확장성이 더 좋습니다. 하나를 선택한 후에는 더 이상 고민하지 마십시오.

Step 4: ad-hoc commands — the green pong that proves everything

ansible all -m ping

이것은 ICMP가 아닙니다. ping module은 SSH 로그인, module 복사, 대상 시스템에서의 Python 실행, 정리 작업을 포함하는 전체 시뮬레이션입니다. 호스트당 하나의 블록이 초록색으로 표시되면 성공입니다:

web1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

초록색 SUCCESS은 인증, Python interpreter, transport가 모두 정상 작동함을 의미합니다. 이 경우 playbook도 정상 작동합니다. 빨간색 UNREACHABLE!은 module 실행 전 transport 단계에서 오류가 발생했음을 의미합니다. 상세한 오류 메시지와 해결 방법은 아래의 failure modes 섹션을 참조하십시오. 추가로 알아두어야 할 ad-hoc command 두 가지는 다음과 같습니다:

ansible all -a "uptime"
ansible all -m apt -a "update_cache=true upgrade=dist" --become

Ad-hoc mode는 일회성 작업이나 점검을 위해 사용합니다. 두 번 이상 실행해야 하는 작업은 playbook로 작성해야 합니다.

Step 5: 첫 번째 playbook — new-VPS checklist as code

이 내용은 새 서버를 구축한 후 처음 10분 동안 수동으로 수행해야 하는 모든 작업입니다. site.yml로 저장하십시오:

---
- name: Baseline a fresh Ubuntu VPS
  hosts: vps
  become: true

  vars:
    deploy_user: deploy
    deploy_pubkey: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
    baseline_packages:
      - fail2ban
      - unattended-upgrades
      - ufw
    baseline_services:
      - fail2ban
      - unattended-upgrades

  tasks:
    - name: Create the deploy user
      ansible.builtin.user:
        name: "{{ deploy_user }}"
        groups: sudo
        append: true
        shell: /bin/bash

    - name: Install the deploy user's SSH key
      ansible.posix.authorized_key:
        user: "{{ deploy_user }}"
        key: "{{ deploy_pubkey }}"

    - name: Passwordless sudo for the deploy user
      ansible.builtin.copy:
        dest: /etc/sudoers.d/deploy
        content: "{{ deploy_user }} ALL=(ALL) NOPASSWD:ALL\n"
        mode: "0440"
        validate: /usr/sbin/visudo -cf %s

    - name: Install baseline packages
      ansible.builtin.apt:
        name: "{{ baseline_packages }}"
        state: present
        update_cache: true

    - name: Enable and start baseline services
      ansible.builtin.service:
        name: "{{ item }}"
        state: started
        enabled: true
      loop: "{{ baseline_services }}"

    - name: Harden sshd with a drop-in
      ansible.builtin.copy:
        dest: /etc/ssh/sshd_config.d/00-hardening.conf
        content: |
          PasswordAuthentication no
          KbdInteractiveAuthentication no
          PermitRootLogin prohibit-password
          X11Forwarding no
        mode: "0644"
        validate: /usr/sbin/sshd -t -f %s
      notify: Restart ssh

    - name: Allow OpenSSH through ufw
      community.general.ufw:
        rule: allow
        name: OpenSSH

    - name: Enable ufw with default deny
      community.general.ufw:
        state: enabled
        policy: deny

  handlers:
    - name: Restart ssh
      ansible.builtin.service:
        name: ssh
        state: restarted

단순 복사보다 이해가 필요한 부분은 다음과 같습니다:

Variablesvars: 아래에 위치하며 "{{ deploy_user }}"를 사용하여 참조합니다. 값이 중괄호({)로 시작하는 경우 YAML 파서의 오류를 방지하기 위해 전체 표현식을 따옴표로 감싸야 합니다. lookup('file', ...)는 실행 시점에 control 머신에서 공개 키를 읽어오므로, playbook에 키 정보가 포함되지 않습니다.

The loop. loop: "{{ baseline_services }}"는 항목당 한 번씩 서비스 작업을 실행하며, 출력 결과에는 각 항목이 개별 줄에 표시됩니다. apt 작업은 패키지 목록 전체를 한 번에 처리한다는 점에 유의하십시오. 하나의 apt 트랜잭션이 더 빠르며 패키지 관리에는 이 방식이 권장됩니다. loop는 한 번에 하나의 항목에만 작용하는 모듈에 사용합니다.

The handler 개념을 숙지해야 합니다. notify: Restart ssh는 "지금 즉시 ssh를 재시작하라"는 의미가 아닙니다. 이는 handler를 대기열에 추가하며, play가 끝날 때 notifying 작업이 실제로 changed를 보고한 경우에만 실행됩니다. 내일 playbook를 다시 실행하면, drop-in 파일은 이미 올바른 상태이고 copy 작업은 ok를 보고하므로 sshd는 재시작되지 않습니다. validate: 라인은 안전장치 역할을 합니다. sshd는 기존 파일을 교체하기 전에 파일을 검사하므로, 오타가 발생하면 데몬이 중단되는 대신 작업이 실패합니다.

PermitRootLogin prohibit-password, no가 아닌 — 의도적인 설정. 이 playbook는 키를 사용하여 root로 로그인합니다. prohibit-password는 사용자의 키 접속은 유지하면서 password root 로그인을 차단합니다. 배포 사용자(ssh deploy@10.0.0.10 sudo trueweb1는 Ansible만 아는 별칭이므로 일반 주소를 사용)가 확인되면, inventory에서 ansible_user=deploy를 변경하고 이후 실행에서 no로 보안을 강화하십시오. 접속이 끊기지 않도록 순서에 따라 보안을 강화해야 합니다.

00- 접두사가 중요합니다. 대부분의 키워드에 대해 sshd는 가장 먼저 파싱된 항목을 적용합니다. Ubuntu의 sshd_config는 본문보다 앞서 어휘 순서에 따라 sshd_config.d/*.conf를 포함합니다. Ubuntu 24.04 cloud 이미지는 이미 해당 디렉토리에 60-cloudimg-settings.conf를 포함하고 있으며, cloud-init을 통해 password 로그인을 허용하는 제공업체는 PasswordAuthentication yes가 포함된 50-cloud-init.conf를 추가합니다. 우리의 설정을 00-hardening.conf으로 명명하면 가장 먼저 정렬되어 두 설정보다 우선 적용됩니다.

Task order는 방화벽의 안전장치입니다. Allow OpenSSH는 deny 정책을 가진 Enable ufw보다 먼저 실행됩니다. Ansible은 나열된 순서대로 작업을 엄격하게 실행하므로, 벽이 세워지기 전에 구멍이 먼저 생기게 됩니다. fail2ban은 여기서 별도의 설정 없이도 유용합니다. Ubuntu 기본 설정은 즉시 sshd를 감시하며, jail의 실제 동작 및 튜닝 방법은 fail2ban on Ubuntu 24.04 guide에서 다룹니다.

Step 6: --check 플래그를 사용한 dry run 실행 후 실제 실행

ansible-playbook site.yml --check

Check mode는 연결을 수행하고 수행될 작업을 계산하지만, 아무것도 변경하지 않습니다. 하단의 PLAY RECAP에서 changed= 개수를 확인하십시오. 이 숫자는 각 host를 수정할 작업의 개수입니다. 주의 사항이 있습니다. 이후의 task가 이전 task의 변경 사항에 의존하는 경우, check mode에는 구조적 한계가 있습니다. Ubuntu 표준 server image에는 ufw가 기본 설치되어 있으므로 이 playbook은 문제없이 dry-run됩니다. 하지만 ufw가 없는 minimal image에서는 check mode에서 ufw task가 실패합니다. check mode는 패키지를 실제로 설치하지 않으므로, module이 호출할 대상이 없기 때문입니다. 이는 dry run의 한계이며 playbook의 버그가 아닙니다. 계획이 올바르면 다음을 실행하십시오.

ansible-playbook site.yml

각 task는 host당 한 줄씩 출력됩니다. 노란색 changed, 초록색 ok로 표시되며, 요약 내용은 다음과 같아야 합니다.

PLAY RECAP *********************************************************************
web1 : ok=10  changed=9  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0
web2 : ok=10  changed=9  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

10개의 ok, 8개의 task, 그리고 handler가 포함됩니다. 귀하의 changed 결과는 본 예제와 1~2개 차이가 날 수 있습니다. Ubuntu 표준 image에는 ufwunattended-upgrades이 기본 설치되어 있으며, fail2ban은 apt가 설치되는 즉시 실행됩니다. 따라서 첫 실행 시 task가 이미 해당 상태가 유지되고 있다고 보고하며 ok를 출력할 수 있습니다. 반드시 0이어야 하는 숫자는 unreachablefailed입니다. become: true에 관한 참고 사항: 이는 root로 접속하는 동안의 형식적인 절차입니다. 하지만 ansible_userdeploy로 변경하는 순간 sudo가 실제로 적용됩니다. 이 playbook이 설치하는 NOPASSWD sudoers 파일 덕분에 -K를 명령줄에 입력하지 않아도 됩니다. 이 설정이 없으면 아래에서 설명할 Missing sudo password 오류가 발생합니다.

Step 7: 두 번 실행하기 — 멱등성의 모습

명령어를 즉시 다시 실행하십시오:

web1 : ok=9  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0

알림(notification)이 발생하지 않은 handler가 실행되지 않았으므로 changed=0ok의 수치가 1씩 감소했습니다. 아무것도 재설치되지 않았고, sshd는 재시작되지 않았으며, ufw도 변경되지 않았습니다. 이것이 playbook을 프로비저너인 동시에 audit 도구로 만드는 이유입니다. 다음 달에 inventory에 web3를 추가하고 다시 실행하십시오. 새 서버는 구축되고, 기존 서버들은 검증됩니다. 수정하지 않은 서버에서 changed가 0이 아닌 값으로 나타나면 drift(구성 이탈)가 발생한 것입니다. 이는 playbook으로 수정해야 할 내용을 누군가 수동으로 편집했음을 의미합니다.

이러한 패턴은 점차 확장됩니다. 다음에 작성할 playbook은 동일한 VPS에 WireGuard VPN 설치를 수행하고, SSH가 터널을 통해서만 응답하도록 ufw 규칙을 강화하는 것입니다. 그 다음에는 모든 app server에 Docker and Compose를 설치하는 playbook을 작성하십시오. site.yml의 결과가 화면을 세 번 넘길 정도로 길어지면 role로 분리하십시오. 하지만 그 전에는 분리하지 마십시오.

Failure modes, with the strings you will see

UNREACHABLE with Permission denied.

web1 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: root@10.0.0.10: Permission denied (publickey).",
    "unreachable": true
}

모든 module이 실행되기 전에 SSH transport가 실패했습니다. ansible_user 설정이 잘못되었거나, 해당 host로 key가 복사되지 않았거나, 잘못된 key를 사용 중입니다. 일반 ssh root@10.0.0.10를 실행한 후, ssh -v를 실행하여 어떤 key가 제공되었는지 확인하십시오. SSH password 접속은 성공하지만 Ansible만 실패한다면, ssh-copy-id 단계를 누락한 것입니다.

Missing sudo password.

web1 | FAILED! => {
    "msg": "Missing sudo password"
}

become: true를 설정하고 non-root user로 접속했으나, 해당 user에게 sudo password가 필요한 상태입니다. 명령줄에 -K (--ask-become-pass)를 추가하거나, 해당 user에게 NOPASSWD sudoers 설정을 부여하십시오. playbook이 deploy로 전환하기 전에 해당 설정을 설치하는 이유가 바로 이것입니다.

error: externally-managed-environment. Ubuntu 24.04의 system Python에 pip를 실행했습니다. 1단계에서 다룬 내용과 같습니다. pip가 아닌 pipx를 사용해야 하며, --break-system-packages도 아닙니다.

mapping values are not allowed in this context.

ERROR! Syntax Error while loading YAML.
  mapping values are not allowed in this context

대부분 들여쓰기 문제입니다. key의 depth가 잘못되었거나, 콜론(:) 뒤에 공백이 없습니다. 보고된 행 번호는 실수한 위치의 근처를 가리키므로, 바로 윗행도 확인하십시오. 유사한 오류인 found character '\t' that cannot start any token는 tab 문자가 포함되었음을 의미합니다. YAML은 tab 문자를 허용하지 않습니다. 실행 전 ansible-playbook site.yml --syntax-check를 습관화하고, 에디터의 YAML 들여쓰기를 2칸(two-space)으로 설정하십시오.

/usr/bin/python3: not found. 표준 Ubuntu 24.04 이미지에서는 드물지만, minimal 또는 netboot 이미지에서는 흔히 발생합니다. 대상 시스템에 Python이 없어 module 실행이 실패하는 경우입니다. raw module을 사용하여 Python을 설치하십시오. 이 module은 원격지에 아무것도 필요하지 않은 유일한 module입니다. ansible all -m raw -a "apt-get update && apt-get install -y python3" --become를 실행한 후 playbook을 다시 실행하십시오.

FAQ

관리 대상 서버에 Ansible을 설치해야 합니까?

아니요. Ansible은 agentless 방식입니다. 제어 머신이 SSH를 통해 작은 Python module을 전송하고, 실행한 뒤, 삭제합니다. 대상 서버에는 python3와 SSH 접속 권한만 있으면 되며, 이는 Ubuntu 기본 이미지에 이미 포함되어 있습니다. 이 가이드에서 설치가 필요한 곳은 제어 머신뿐입니다.

Ansible에서 "Permission denied (publickey)" 오류가 발생하는 이유는 무엇입니까?

Permission denied (publickey)가 포함된 UNREACHABLE! 블록은 Ansible이 실행되기 전 SSH 인증에 실패했음을 의미합니다. inventory의 ansible_user가 실제 설정한 계정과 일치하는지, 해당 호스트로 ssh-copy-id를 실행했는지, 그리고 일반적인 ssh user@host 접속이 비밀번호 없이 가능한지 확인하십시오. 일반 ssh 명령어가 해결되면 Ansible도 해결됩니다. 두 방식은 동일한 전송 방식을 사용하기 때문입니다.

Ansible에서 idempotent란 무엇을 의미합니까?

Task는 수행할 동작 대신 "이 package가 존재함", "이 line이 이 file에 있음"과 같은 원하는 상태를 선언합니다. 이미 해당 상태가 유지되고 있다면, Ansible은 아무 작업도 수행하지 않고 changed 대신 ok를 보고합니다. 따라서 playbook를 두 번 실행하면 두 번째에는 changed=0이 표시되며, 재실행이 위험한 재설치가 아닌 안전한 감사(audit)가 되는 것입니다.

Ubuntu 24.04에서 Ansible을 설치할 때 pip와 pipx 중 무엇을 사용해야 합니까?

pipx를 사용하십시오. Ubuntu 24.04는 시스템 Python을 외부 관리 대상으로 지정하므로, 설계상 pip install ansibleerror: externally-managed-environment 오류를 발생시킵니다. pipx install --include-deps ansible는 Ansible을 격리된 virtualenv에 설치하며, ansible, ansible-playbook 및 기타 명령어를 PATH에 깔끔하게 등록합니다.

ansible와 ansible-core 패키지의 차이점은 무엇입니까?

ansible-core은 엔진과 ansible.builtin module만 포함합니다. ansible 패키지는 core와 엄선된 community collections를 함께 제공합니다. 여기에는 이 가이드에서 사용하는 ansible.posix (authorized_key module) 및 community.general (ufw module)가 포함됩니다. 처음에는 전체 패키지로 시작하십시오. core와 필요한 collection만 남기는 방식은 명확한 이유가 있을 때만 수행하십시오.