- Docker 학습 및 실습 기록 정리
문서 목적
이 문서는 내가 실제로 수행한 Docker 관련 실습 이력과 질의 내용을 바탕으로, 어떤 강의/실습을 진행했고 무엇을 익혔는지 복기하기 위한 기록용 문서다.
정리 범위는 다음을 포함한다.
- Ubuntu / Rocky(CentOS 계열) 환경에서의 Docker 설치
- 이미지, 컨테이너, 네트워크, 볼륨, 리소스 제한 실습
- Dockerfile / 이미지 빌드 / Commit / Save / Load / Push / Pull
- Harbor 사설 레지스트리 구성 및 insecure registry 처리
- WordPress + MariaDB compose 실습
- nGrinder Controller / Agent 실행 경험
- cAdvisor 기반 컨테이너 모니터링
- 추가로 실무에서 알아두면 좋은 Docker 운영 팁
- 성능 테스트 및 부하 실습 시 유용한 체크 포인트
- 이번 학습에서 진행한 강의 흐름
1-1. Docker 설치 및 환경 준비
처음에는 Ubuntu와 Rocky/CentOS 계열에서 Docker를 설치하는 방법부터 실습했다.
Ubuntu 계열에서 익힌 내용
- apt 기반 패키지 설치
- Docker 공식 저장소 등록
- GPG 키 등록
- docker.io 설치와 docker-ce 설치 차이 확인
- Docker daemon 기동 문제 확인
systemctl start dockersystemctl restart dockerdocker version,docker ps로 정상 동작 확인
Rocky/CentOS 계열에서 익힌 내용
dnf-plugins-core설치- Docker 공식 CentOS 저장소 추가
docker-ce,docker-ce-cli,containerd.io설치systemctl enable --now docker로 서비스 등록 및 기동
여기서 배운 핵심
- Docker는 단순 설치보다 daemon이 정상 기동되는지 확인하는 것이 중요하다.
- 리눅스 배포판별로 패키지 관리자와 저장소 등록 방식이 다르다.
- 설치 직후 확인 기본 명령은 아래 순서로 잡아두면 좋다.
1
2
3
4
docker version
docker info
docker ps
systemctl status docker
1-2. 기본 이미지 및 컨테이너 생명주기 실습
설치 이후에는 Docker의 가장 기본 개념인 이미지와 컨테이너를 반복적으로 다뤘다.
수행한 실습
docker search nginxdocker image pull nginxdocker image inspect nginxdocker container createdocker startdocker stopdocker pausedocker unpausedocker rm -fdocker logsdocker exec -it
여기서 익힌 개념
- 이미지(Image) 는 실행 가능한 템플릿
- 컨테이너(Container) 는 이미지를 실행한 인스턴스
create와run의 차이create: 생성만run: 생성 + 시작
exec는 실행 중인 컨테이너 내부 진입logs는 컨테이너 표준 출력 확인inspect는 상세 메타데이터 확인
실제로 손에 익힌 명령 예시
1
2
3
4
5
6
7
8
docker image pull nginx
docker container create -p 80:80 --name webserver nginx
docker start webserver
docker ps
docker logs webserver
docker exec -it webserver /bin/bash
docker stop webserver
docker rm -f webserver
1-3. 포트 포워딩과 웹 서비스 확인
Nginx 컨테이너를 띄운 뒤 호스트와 컨테이너 간 포트 연결을 실습했다.
수행한 실습
-p 80:80-p 8080:80-p 8181:80-P로 랜덤 포트 매핑curl http://localhost:80curl http://호스트IP:포트
여기서 배운 핵심
- 왼쪽은 호스트 포트, 오른쪽은 컨테이너 포트다.
- 컨테이너 내부 서비스 포트를 잘못 지정하면 접속이 안 된다.
- 예를 들어 nginx는 기본적으로 80 포트를 쓰므로
-p 8081:88은 내부 88 포트에 프로세스가 없으면 실패한다. - 브라우저 접속 문제는 보통 아래 네 가지를 먼저 본다.
- 컨테이너가 실행 중인지
- 포트 매핑이 맞는지
- 서비스가 해당 포트를 실제 listen 중인지
- 방화벽/보안그룹 문제가 없는지
확인 명령
1
2
3
4
5
docker ps
docker port webserver
docker logs webserver
ss -ltnp
curl http://127.0.0.1:8080
1-4. 컨테이너 상태, 로그, 프로세스 관찰
실행 중인 컨테이너가 어떻게 동작하는지 관찰하는 명령도 많이 다뤘다.
수행한 실습
docker statsdocker topdocker logs -fdocker ps -a --formatdocker container ls -a --format
배운 내용
docker stats는 CPU/메모리/네트워크/블록 I/O 를 실시간으로 확인할 수 있다.docker top은 컨테이너 내부 프로세스 확인에 유용하다.docker logs -f는 애플리케이션 기동 로그 추적에 필수다.--format "table ..."은 운영 중 목록을 보기 좋게 정리할 때 좋다.
자주 쓰는 예시
1
2
3
4
docker stats webserver
docker top webserver
docker logs -f webserver
docker ps -a --format "table \t\t"
1-5. 실행 방식 차이와 프로세스 유지 방식 이해
CentOS 계열 이미지를 띄우면서 컨테이너가 왜 바로 종료되는지 확인하는 실습도 진행했다.
수행한 실습
/bin/cal/bin/bashtail -f /dev/null
배운 핵심
- 컨테이너는 PID 1 프로세스가 종료되면 같이 종료된다.
centos:8이미지를docker run -d centos:8로만 띄우면 보통 즉시 종료된다.- 계속 살아 있어야
docker exec로 들어갈 수 있다. - 테스트용으로는 아래 패턴이 많이 쓰인다.
1
2
docker run -d --name test1 centos:8 tail -f /dev/null
docker exec -it test1 /bin/bash
이 부분은 이후 네트워크 실습이나 디버깅 컨테이너 운용에 매우 중요하다.
1-6. 컨테이너 간 통신과 Docker Network 실습
브리지 네트워크를 직접 만들고 컨테이너들을 연결하는 실습도 수행했다.
수행한 실습
docker network create --driver bridge ... test_bridgedocker network connectdocker network disconnectdocker network inspect bridge- 사용자 정의 bridge 네트워크에 test1, test2 연결
- 컨테이너 이름 기반 통신 확인
여기서 배운 핵심
- 기본 bridge 보다 사용자 정의 bridge 네트워크가 실무에서 더 유용하다.
- 같은 사용자 정의 네트워크에 있는 컨테이너는 보통 컨테이너 이름으로 DNS 해석이 가능하다.
- 컨테이너 간 통신 테스트용으로
ping,curl,exec를 사용한다. - 네트워크 이름 오타(
test_brigde) 같은 사소한 실수가 컨테이너 기동 실패의 흔한 원인이다.
실무 관점 요약
- 단일 서버에서 여러 컨테이너를 묶어 서비스하려면 사용자 정의 bridge 네트워크를 우선 고려한다.
- 애플리케이션 간 통신 시 IP보다 서비스명/컨테이너명을 쓰는 습관이 관리에 유리하다.
1-7. 볼륨과 바인드 마운트 실습
컨테이너의 데이터 영속성을 다루기 위해 -v 옵션과 Docker volume을 실습했다.
수행한 실습
-v /tmp:/usr/share/nginx/html- 호스트 index.html 수정 후 nginx 반영 확인
docker volume create my-voldocker inspect volume my-vol/var/lib/docker/volumes/.../_data확인- WordPress/MariaDB 용 volume 생성
배운 핵심
- 바인드 마운트: 호스트 디렉터리를 직접 연결
- 볼륨: Docker가 관리하는 영속 저장소
- 정적 파일 실습에는 바인드 마운트가 직관적이다.
- DB, 운영 데이터에는 named volume이 더 안전하고 관리가 쉽다.
비교
| 구분 | 바인드 마운트 | Docker Volume | |—|—|—| | 연결 대상 | 호스트 실제 경로 | Docker 관리 저장소 | | 사용 편의성 | 직관적 | 운영 안정성 좋음 | | 개발 편의성 | 높음 | 보통 | | 운영 권장도 | 제한적 | 높음 |
1-8. 컨테이너 내부 파일 반입/반출과 변경 추적
컨테이너와 호스트 사이 파일을 복사하고, 변경사항을 이미지화하는 흐름도 실습했다.
수행한 실습
docker cpdocker container diffdocker container commitdocker image save -odocker image load -itar tf
배운 핵심
docker cp로 컨테이너 내부 파일을 가져오거나 넣을 수 있다.docker diff는 컨테이너에서 변경된 파일을 확인할 수 있다.docker commit은 컨테이너 상태를 이미지로 저장하지만, 실무 표준으로는 재현성과 이력 관리 때문에 Dockerfile 기반 빌드가 더 바람직하다.save/load는 인터넷이 안 되는 환경이나 이미지 전달이 필요한 상황에서 유용하다.
실무 판단 기준
- 급한 임시 백업/스냅샷:
commit - 정식 배포/재현 가능한 빌드:
Dockerfile - 폐쇄망 전달:
docker save+docker load
1-9. Dockerfile 작성과 사용자 정의 이미지 빌드
직접 Dockerfile을 만들고 이미지를 빌드하는 실습도 진행했다.
수행한 실습
docker build -t hello:v1.0 .docker image build --tag web-site:v1.0 ./- Nginx/Tomcat 기반 이미지 제작
- HTML 파일 포함 이미지 빌드
- 잘못된 태그(
v.1.0) 등 오류 수정
여기서 배운 핵심
- Dockerfile은 컨테이너를 사람이 반복 가능하게 만드는 선언문이다.
docker commit보다 Dockerfile이 협업과 유지보수에 유리하다.- 파일 복사, 포트 공개, 기동 명령 정의를 통해 애플리케이션 이미지를 만들 수 있다.
아주 기본적인 예시
1
2
3
FROM nginx:latest
COPY ./html /usr/share/nginx/html
EXPOSE 80
이 단원에서 익힌 실무 감각
- 빌드 컨텍스트(
.) 범위를 이해해야 한다. - Dockerfile, 정적 파일, 설정 파일 위치가 빌드 성공에 직접 영향을 준다.
- 태그 네이밍 규칙을 통일하면 배포 추적이 쉬워진다.
1-10. 이미지 태깅, Docker Hub Push/Pull
직접 만든 이미지를 태깅하고 원격 저장소에 업로드/다운로드하는 실습도 수행했다.
수행한 실습
docker logindocker image tagdocker pushdocker pull
배운 핵심
- 이미지명 형식은 보통 아래 구조를 따른다.
1
레지스트리주소/네임스페이스/이미지명:태그
예:
1
2
docker.io/shguddnr3/hello:v1.0
172.16.0.128/my-repo/hello:v1.0
- 태그는 단순 별칭이 아니라 배포 단위 식별자다.
latest만 쓰는 습관은 운영 추적성을 떨어뜨릴 수 있다.- 최소한
v1.0.0,20260407,git-sha중 하나는 전략적으로 쓰는 게 좋다.
1-11. Harbor 사설 레지스트리 구축 실습
Harbor 오프라인 설치 파일을 사용해 사설 레지스트리를 구성하는 실습도 진행했다.
수행한 실습
harbor-offline-installer-v2.15.0.tgz복사- 압축 해제
harbor.yml.tmpl→harbor.yml- hostname, port 설정
- https 주석 처리
./install.shdocker restart nginxdaemon.json에 insecure registry 등록- Harbor 로그인 후 이미지 push
배운 핵심
- Harbor는 사설 이미지 저장소이자 프로젝트 단위 이미지 관리 도구다.
- HTTPS 없이 쓰는 경우 Docker daemon에 insecure registry 설정이 필요하다.
- 잘못된 IP (
176...오타 등) 나 http/https 처리 문제는 push 실패의 흔한 원인이다. - 폐쇄망/사내망 환경에서는 Harbor 가치가 매우 크다.
정리된 설정 예시
1
2
3
{
"insecure-registries": ["172.16.0.128"]
}
Harbor 실습에서 얻은 실무 포인트
- 사설 레지스트리 주소, 포트, 인증 정보는 팀 표준화가 중요하다.
- 이미지 업로드 전 반드시 정확한 레지스트리 주소로
docker tag해야 한다. - 사설 레지스트리를 쓰면 버전 통제, 폐쇄망 배포, 내부 표준 이미지 관리가 쉬워진다.
1-12. Docker Compose 기반 다중 컨테이너 실습
WordPress + MariaDB 를 구성하면서 Compose 기반 다중 컨테이너 실행을 익혔다.
수행한 실습
docker-compose.yml작성- named volume 생성
docker compose up -d- mariadb 컨테이너 접속
- wordpress 컨테이너와 DB 컨테이너 동시 기동
배운 핵심
- Compose는 관련 있는 컨테이너를 하나의 서비스 단위로 관리하게 해준다.
- 웹, DB, 볼륨, 네트워크를 선언형으로 관리할 수 있다.
- 단일
docker run명령보다 재현성과 공유성이 훨씬 좋다.
실무 관점
- 개발/검증 환경에서는 compose가 매우 강력하다.
- 다만 운영 대규모 환경에서는 Kubernetes, Swarm, ECS 등 상위 오케스트레이션이 필요할 수 있다.
1-13. 리소스 제한 및 운영 제어 실습
컨테이너 CPU와 메모리를 제한하고 실행 정책을 조정하는 실습도 진행했다.
수행한 실습
--cpus--memory--memory-swapdocker update--restart always- 재기동 후 동작 확인
익힌 핵심
- 컨테이너는 기본적으로 호스트 자원을 적극 사용할 수 있으므로 제한이 필요하다.
- 서비스 특성에 맞춰 CPU/메모리 상한을 둬야 장애 전파를 줄일 수 있다.
docker update로 실행 중 컨테이너의 자원 제한을 조정할 수 있다.--restart always는 서버 재부팅 후 자동 복구에 유용하다.
예시
1
2
docker container update --cpus 0.25 --memory 128m --memory-swap 128m wordpress
docker update --restart always webserver1
1-14. 모니터링 도구 cAdvisor 실습
컨테이너 자원 사용량 관찰을 위해 cAdvisor도 실행했다.
수행한 실습
- cAdvisor 컨테이너 실행
- 볼륨/시스템 경로 마운트
- 웹 UI 기반 관찰
배운 핵심
docker stats는 즉시 확인용- cAdvisor 는 시각화/관찰용
- 실제 운영에서는 여기에 Prometheus, Grafana 를 붙여 장기 모니터링을 구성하는 경우가 많다.
1-15. nGrinder Controller / Agent 실행 실습
성능 테스트 도구 측면에서는 nGrinder 구성도 시도했다.
수행한 실습
ngrinder/controllerngrinder/controller:3.5.5-p1- Controller 포트 다중 매핑
- Agent 실행
--link controller:controller
여기서 배운 핵심
- 성능 테스트 도구는 단순 실행보다 포트 구조와 컨트롤러-에이전트 연결 이해가 중요하다.
- 컨테이너명 충돌, 포트 중복, 이미지 태그 지정 문제를 직접 겪으며 수정했다.
- 성능 도구는 Docker로 빠르게 구성 가능하지만, 포트/네트워크 정리가 선행되어야 한다.
- 이번 학습을 통해 실제로 익힌 Docker 역량 정리
2-1. 기초 역량
- Docker 설치 및 서비스 기동
- 이미지 검색 / 다운로드 / 조회
- 컨테이너 생성 / 시작 / 종료 / 삭제
- 로그 확인 / 내부 접속 / 상태 점검
2-2. 운영 기초 역량
- 포트 매핑과 외부 접속 확인
- 리소스 제한(CPU, Memory, Swap)
- 재시작 정책 설정
- 컨테이너 포맷 출력 / 프로세스 확인 / 실시간 자원 관찰
2-3. 스토리지 역량
- 바인드 마운트 사용
- Docker Volume 생성 및 조회
- 영속 데이터 관리 기본 개념 이해
2-4. 이미지 관리 역량
- Dockerfile 작성
- 이미지 빌드 / 태깅 / 저장 / 로드
- Docker Hub Push/Pull
- Harbor Push/Pull
2-5. 네트워크 역량
- bridge 네트워크 이해
- 사용자 정의 네트워크 생성
- 컨테이너 간 통신
- 네트워크 연결/분리
2-6. 실전 응용 역량
- WordPress + MariaDB compose 구성
- Harbor 사설 레지스트리 구축
- nGrinder Controller/Agent 기동
- cAdvisor 기반 모니터링
- 이번 실습을 강의 관점으로 재구성하면
강의 1. Docker 설치와 구조 이해
- Docker 엔진 설치
- daemon과 client 개념
- 배포판별 설치 차이
- 설치 검증
강의 2. 이미지와 컨테이너 기본
- image / container 차이
- search / pull / run / exec / logs / inspect
- create 와 run 차이
강의 3. 웹 서버 컨테이너 실행
- nginx 실행
- 포트 포워딩
- curl 확인
- 문제 원인 분석
강의 4. 컨테이너 관찰과 디버깅
- stats / top / logs -f / inspect
- 종료 원인 파악
- PID 1 이해
강의 5. Docker 네트워크
- bridge 네트워크
- 사용자 정의 네트워크
- 컨테이너 간 이름 기반 통신
강의 6. 볼륨과 데이터 영속화
- bind mount
- docker volume
- 정적 파일 변경 반영
- DB 데이터 보존 개념
강의 7. 이미지 커스터마이징
- docker cp / diff / commit
- save / load
- Dockerfile build
- 이미지 태깅
강의 8. 레지스트리와 배포
- Docker Hub push/pull
- Harbor 설치
- insecure registry 처리
- 사설 이미지 저장소 운영
강의 9. Compose와 다중 컨테이너
- compose 작성
- WordPress + MariaDB
- 볼륨/네트워크/환경변수
강의 10. 운영과 성능 관찰
- restart 정책
- CPU/메모리 제한
- cAdvisor
- nGrinder 구성
- 실습 중 자주 마주친 실수와 그 의미
오타 기반 실수
dockwr,docekr,docmer,dockeloaclhosttest_brigde176.16.0.128- 포트 매핑 방향 실수
- 잘못된 이미지 태그 표기
의미
이런 실수는 Docker를 모르는 문제라기보다, CLI 기반 작업에서 문자 하나 차이로 완전히 다른 결과가 나오는 운영 특성을 체감한 것이다.
개선 팁
- 복잡한 명령은 히스토리 재사용
docker ps,docker logs,docker inspect를 디버깅 1순위로 사용- 긴 명령은 쉘 스크립트 또는 compose 파일로 관리
- 실무에서 추가로 꼭 알아두면 좋은 Docker 기능
5-1. docker info
엔진 상태, storage driver, cgroup driver, registry 설정 등을 한 번에 확인할 수 있다.
1
docker info
5-2. docker system df
이미지, 컨테이너, 볼륨이 디스크를 얼마나 차지하는지 확인할 수 있다.
1
docker system df
5-3. 정리 명령
불필요한 리소스 정리에 매우 중요하다.
1
2
3
4
5
docker image prune
docker container prune
docker volume prune
docker network prune
docker system prune -a
주의: 운영 환경에서는 삭제 범위를 반드시 확인하고 사용해야 한다.
5-4. docker inspect --format
JSON 전체를 보기보다 필요한 값만 뽑아보는 습관이 중요하다.
1
2
docker inspect --format=' ' webserver
docker inspect --format='' webserver
5-5. 환경변수 주입
DB 계정, 포트, 애플리케이션 옵션은 환경변수로 분리하는 습관이 중요하다.
1
docker run -e SPRING_PROFILES_ACTIVE=dev my-app
Compose에서는 .env 와 함께 사용하면 더 편하다.
5-6. Healthcheck
컨테이너가 떠 있다고 해서 서비스가 정상인 것은 아니다. 헬스체크를 넣어야 운영 신뢰도가 올라간다.
1
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost/ || exit 1
5-7. 로그 정책
로그가 무한히 쌓이면 디스크 장애가 발생할 수 있다. 로그 드라이버와 로테이션 설정을 알아두는 것이 좋다.
예:
1
docker run --log-opt max-size=10m --log-opt max-file=3 nginx
5-8. 멀티 스테이지 빌드
애플리케이션 이미지를 가볍게 만들 때 중요하다. 특히 Java, Node.js, Go 에서 효과가 크다.
예시 개념:
- build stage에서 컴파일
- runtime stage에는 실행 결과물만 복사
5-9. .dockerignore
불필요한 파일이 이미지 빌드 컨텍스트에 포함되지 않게 해야 한다. 빌드 속도와 보안에 모두 중요하다.
예:
1
2
3
4
5
6
.git
.idea
node_modules
build
target
*.log
5-10. 컨테이너 보안 기본
- 가능하면 root 사용자로 실행하지 않기
- 최신 태그 대신 고정 버전 태그 사용
- 불필요한 포트 노출 금지
- 비밀값은 이미지에 박지 말고 환경 변수/시크릿으로 분리
- 운영에서는 read-only filesystem, capability drop 도 검토
- 성능 테스트 및 부하 테스트 관점에서 유용한 팁
6-1. 테스트 대상과 도구를 같은 서버에 두면 왜곡될 수 있다
nGrinder나 부하 도구를 대상 시스템과 같은 서버에 올리면, CPU/메모리/네트워크 경쟁 때문에 결과가 왜곡될 수 있다.
권장
- 가능하면 부하 생성기와 대상 시스템 분리
- 최소한 자원 사용량을 동시에 관찰
docker stats,top,free -h,lscpu로 함께 확인
6-2. 컨테이너 자원 제한 상태를 먼저 기록하자
테스트 결과를 해석하려면 아래 값이 먼저 기록되어야 한다.
- 컨테이너 CPU 제한
- 메모리 제한
- 스왑 허용 여부
- 호스트 전체 CPU / RAM
- 대상 서비스 포트와 네트워크 구조
이 정보 없이 TPS, 응답속도만 보면 결과를 잘못 해석하기 쉽다.
6-3. 테스트 전 체크리스트
1
2
3
4
5
6
docker ps
docker stats
docker inspect <container>
free -h
lscpu
ss -ltnp
6-4. 테스트 중 반드시 볼 항목
- CPU 사용률
- 메모리 사용률
- OOM 발생 여부
- 로그 에러 증가 여부
- 응답 지연 증가 시점
- 컨테이너 재시작 여부
- DB 연결 수
- 네트워크 병목
6-5. 테스트 후 남겨야 할 기록
- 사용한 이미지 태그
- 실행 옵션(
--cpus,--memory,-p,-v) - 테스트 시간대
- 동시 사용자 수 / 스레드 수
- 대상 URL / API
- 평균/최대 응답시간
- 에러율
- 서버 측 로그
이런 기록이 있어야 다음 테스트와 비교가 가능하다.
6-6. nGrinder 실습 시 추가 팁
- Controller와 Agent 간 포트 정책을 먼저 정리
- 방화벽/보안 설정 확인
- 컨테이너 이름 충돌 주의
- 포트 중복 사용 여부 먼저 점검
- 결과를 볼 때는 대상 서버 자원 그래프와 같이 봐야 한다
- 앞으로 더 학습하면 좋은 주제
우선순위 높음
- Docker Compose 심화
- depends_on
- healthcheck
- env_file
- network / volume 분리
- Dockerfile 최적화
- 레이어 캐시
- 멀티 스테이지 빌드
- 이미지 경량화
- 운영 관점 로그/모니터링
- cAdvisor + Prometheus + Grafana
- 보안
- rootless
- 이미지 취약점 스캔
- secrets 관리
- 사설 레지스트리 운영
- Harbor 프로젝트/권한/로봇계정
- 성능 테스트 체계화
- nGrinder 시나리오 작성
- 테스트 데이터 관리
- 병목 구간 분석
다음 단계로 연결 가능한 기술
- Kubernetes
- Helm
- GitHub Actions / Jenkins 기반 이미지 빌드 자동화
- Blue/Green / Rolling 배포
- Spring Boot 애플리케이션 컨테이너 표준화
- 한 줄 요약
이번 실습은 단순 Docker 명령어 암기가 아니라, 설치 → 컨테이너 실행 → 네트워크/볼륨/이미지 관리 → 레지스트리 → compose → 자원 제어 → 성능 테스트 도구 연결 까지 이어지는 꽤 넓은 범위의 입문~중급 과정이었다.
특히 다음 역량이 실제로 손에 익은 점이 의미 있다.
- Docker 기본 명령을 직접 반복 수행함
- 오류를 보고 원인을 수정하는 흐름을 경험함
- 이미지/컨테이너/네트워크/볼륨의 연결 구조를 체감함
- Harbor, Compose, nGrinder, cAdvisor 같은 응용 주제까지 확장함
즉, 이번 학습은 Docker를 단순히 “써본 수준”이 아니라, 실무에서 테스트 환경과 사내 서비스 환경을 직접 올려볼 수 있는 수준으로 가는 과정으로 정리할 수 있다.