Rocky Linux 9 busctl 명령어 완벽 가이드: 기본 사용법부터 systemd 디버깅까지

시스템 관리자로서 우리는 모두 그런 경험을 해본 적이 있습니다. 중요한 서비스가 예기치 않게 오작동하고, systemctl status
는 그저 '실패(failed)'라는 무심한 메시지만 보여줍니다. 로그 파일은 모호하거나 아예 아무런 단서도 제공하지 않습니다. 마치 잠긴 방 안에서 무슨 일이 일어나는지 알 수 없는 것처럼 답답한 상황이죠. 하지만 만약 그 방의 벽에 귀를 대고 내부의 모든 대화를 엿들을 수 있다면 어떨까요? 더 나아가, 문을 열고 들어가 직접 명령을 내릴 수 있다면 어떨까요?
현대의 리눅스 시스템, 특히 Rocky Linux 9과 같은 엔터프라이즈 배포판은 독립된 프로세스들의 단순한 집합이 아닙니다. 이것은 서로 끊임없이 대화하고 협력하는 복잡하고 정교한 사회입니다. 그리고 이 모든 통신의 중심에는 D-Bus(Desktop Bus)가 있습니다. D-Bus는 운영체제의 '중앙 신경 시스템' 또는 '디지털 우체국'과 같습니다. 네트워크 관리자부터 전원 설정, 사용자 로그인에 이르기까지 모든 것이 이 버스를 통해 메시지를 주고받으며 조화롭게 작동합니다.
이 강력한 통신 백본과 상호작용하기 위한 현대적이고 필수적인 도구가 바로 busctl
입니다. systemd
와 함께 탄생한 busctl
은 과거의 dbus-send
와 같은 도구들보다 훨씬 직관적이고 강력한 기능을 제공하며, systemd
기반 시스템의 핵심 제어 도구로 자리 잡았습니다.
이 가이드의 목표는 명확합니다. 여러분이 Rocky Linux 9 시스템 내부에서 벌어지는 비밀스러운 대화를 엿듣고, 분석하고, 나아가 직접 지휘할 수 있도록 busctl
의 모든 것을 알려드리는 것입니다. 우리는 수동적인 문제 해결사에서 능동적인 시스템 지휘자로 거듭나게 될 것입니다.
핵심 요약 (Key Takeaways)
- D-Bus는 현대 리눅스의 표준 프로세스 간 통신(IPC) 시스템입니다.
busctl
은 D-Bus 서비스와 상호작용하기 위한 필수 명령줄 도구입니다.busctl
을 마스터하면 시스템 서비스를 근본적인 수준에서 검사, 디버깅, 자동화할 수 있습니다.- 이 가이드는 초심자를 위한 기본 개념부터 전문가를 위한 고급 문제 해결까지 모든 것을 다룹니다.
시스템 관리자를 위한 입문: D-Bus란 정확히 무엇인가?
busctl
을 배우기 전에, 우리는 그것이 소통하는 대상인 D-Bus를 먼저 이해해야 합니다. 이 섹션은 D-Bus의 '왜'와 '무엇'을 명확히 설명하여, IT 학생, 비전공자, 주니어 엔지니어가 탄탄한 개념적 기반을 다질 수 있도록 설계되었습니다.
파이프와 소켓을 넘어: D-Bus가 존재하는 이유
초기 유닉스 철학은 "하나의 작은 도구가 한 가지 일을 잘 처리하고, 텍스트 스트림을 통해 통신한다"는 것이었습니다. 이는 매우 강력했지만, 시스템이 복잡해지면서 한계에 부딪혔습니다. 데스크톱 환경과 시스템 서비스들은 단순한 텍스트 이상의 구조화된 데이터, 타입이 보장되는 원격 함수 호출, 비동기 이벤트 알림 같은 정교한 통신 방법이 필요했습니다.
D-Bus는 이러한 요구에 부응하기 위해 등장했습니다. 과거 GNOME의 CORBA나 KDE의 DCOP와 같이 파편화되어 있던 IPC 솔루션들을 대체할 단일 표준으로 설계되었습니다. D-Bus는 단순한 데이터 전송 채널이 아니라, 잘 정의된 규칙과 구조를 가진 '소프트웨어 버스' 추상화를 제공하는 메시지 지향 미들웨어입니다. 이는 시스템 설계 패러다임의 근본적인 전환을 의미합니다. 즉, 단순한 텍스트 스트림의 유연성보다 형식화된 API의 신뢰성과 발견 가능성을 우선시하는 방향으로 나아간 것입니다. 엔터프라이즈 환경에서 이는 예측 가능하고 확장 가능한 대규모 시스템 관리를 가능하게 하는 핵심적인 변화였습니다.
두 개의 주요 채널: 시스템 버스와 세션 버스
D-Bus는 주로 두 개의 버스를 통해 작동하며, dbus-daemon
이라는 프로세스가 이들 버스의 우체국장처럼 메시지 라우팅을 담당합니다.
- 시스템 버스 (
/run/dbus/system_bus_socket
): '정부 채널'에 비유할 수 있습니다. NetworkManager, systemd, logind, Polkit 등 시스템 전반에 영향을 미치는 핵심 서비스들이 이 버스를 사용합니다. 모든 사용자가 (권한 내에서) 접근할 수 있습니다. - 세션 버스 (
/run/user/<uid>/bus
): '개인 채널'과 같습니다. 사용자가 로그인할 때마다 생성되며, 해당 사용자의 데스크톱 애플리케이션, 알림, 사용자별 서비스 간의 통신에 사용됩니다. 다른 사용자의 세션 버스에는 접근할 수 없습니다.
D-Bus의 언어: 웹 서비스 비유로 이해하기
D-Bus의 구성 요소들은 처음에는 추상적으로 느껴질 수 있습니다. 하지만 웹 서비스에 비유하면 놀라울 정도로 쉽게 이해할 수 있습니다. 이 비유는 초심자가 D-Bus의 계층 구조를 직관적으로 파악하는 데 매우 중요합니다.
D-Bus 개념: 웹 서비스 비유
이 표는 5개의 추상적인 D-Bus 기술 용어를 친숙한 웹 개념에 매핑하여 초보자가 기존의 정신 모델을 활용하여 D-Bus 계층을 빠르게 파악할 수 있도록 합니다. 이는 정의 목록을 직관적이고 관계적인 지도로 변환하여 인지 부하를 크게 줄이고 학습을 가속화합니다.
D-Bus 개념 | 웹 서비스 비유 | 설명 및 예시 |
---|---|---|
서비스 이름 | 도메인 이름 (예: google.com ) |
버스에 있는 프로그램을 식별하는 고유하고 잘 알려진 이름입니다. 예: org.freedesktop.NetworkManager . |
객체 경로 | URL 경로 (예: /search ) |
서비스가 외부에 노출하는 특정 "리소스" 또는 객체입니다. 예: /org/freedesktop/NetworkManager/ActiveConnection/1 . |
인터페이스 | API 엔드포인트 그룹 (예: SearchAPI ) |
클래스나 API 계약처럼, 메서드와 시그널의 명명된 집합입니다. 예: org.freedesktop.NetworkManager.Connection.Active . |
메서드 | HTTP 동사 + 함수 (예: POST /search ) |
객체에 대해 호출할 수 있는 함수로, 인자를 받고 값을 반환할 수 있습니다. 예: GetUnit 메서드. |
시그널 | 웹훅 / 알림 | 어떤 이벤트(예: 상태 변경)가 발생했을 때 객체가 내보내는 방송 메시지입니다. 호출하는 것이 아니라, 수신 대기하는 것입니다. 예: PropertiesChanged 시그널. |
busctl
필드 매뉴얼: 시스템 관찰자에서 시스템 지휘자로
이제 D-Bus의 개념을 이해했으니, 실제 도구인 busctl
을 사용할 시간입니다. 이 섹션은 즉시 도구를 사용하고자 하는 엔지니어를 위한 실용적인 핵심 가이드입니다. 각 명령어는 명확한 설명, 구문, 그리고 systemd
나 NetworkManager
를 사용한 실제 예제와 함께 제공됩니다.
발견 - D-Bus 환경 지도 그리기
가장 먼저 할 일은 우리 시스템에 어떤 서비스들이 있는지 파악하는 것입니다.
busctl list
: 시스템 및 세션 버스에서 현재 실행 중인 모든 서비스를 나열합니다. 이 명령을 통해 고유 이름(:1.123
형식)과 잘 알려진 이름(org.freedesktop...
형식)의 차이를 이해할 수 있습니다. 고유 이름은 버스에 연결될 때마다 할당되는 임시 주소이며, 잘 알려진 이름은 특정 서비스를 가리키는 영구적인 이름입니다.# 시스템 버스에 연결된 모든 서비스 보기 busctl list # 현재 사용자의 세션 버스에 연결된 서비스 보기 busctl --user list
--unique
,--acquired
(잘 알려진 이름만 표시),--activatable
(아직 활성화되지 않았지만 호출 시 자동 시작될 수 있는 서비스만 표시)과 같은 유용한 옵션으로 출력을 필터링할 수 있습니다.busctl tree [서비스]
: 특정 서비스의 객체 계층을 시각화합니다. 파일 시스템의 디렉터리 구조를 보는 것과 유사하여 서비스의 전체 구조를 파악하는 데 매우 유용합니다.# NetworkManager 서비스의 객체 트리 보기 busctl tree org.freedesktop.NetworkManager
검사 - 서비스 내부 들여다보기
서비스를 발견했다면, 이제 그 안을 자세히 들여다볼 차례입니다.
busctl status
: 서비스의 프로세스 정보(PID, 사용자, 보안 컨텍스트 등)를 가져옵니다. 어떤 프로세스가 실제로 해당 서비스를 제공하고 있는지 확인할 때 필수적입니다.# systemd의 로그인 관리자(logind) 서비스 상태 확인 busctl status org.freedesktop.login1
busctl introspect <서비스> <객체 경로>
: 가장 강력한 검사 도구입니다. 실행 중인 서비스의 "API 문서"를 실시간으로 읽는 것과 같습니다. 이 명령은 해당 객체가 제공하는 모든 인터페이스, 메서드(와 그 인자들), 시그널, 속성을 보여줍니다.# systemd의 메인 객체 내부 들여다보기 busctl introspect org.freedesktop.systemd1 /org/freedesktop/systemd1
상호작용 - 시스템 상태 읽고 쓰기
이제 시스템의 상태를 읽고 변경하는, busctl
의 진정한 힘을 경험해 보겠습니다.
busctl get-property <서비스> <객체> <인터페이스> <속성>
: 서비스로부터 특정 값을 읽어옵니다.튜토리얼: NetworkManager 연결이 데이터 사용량에 제한이 있는 종량제(metered)인지 확인하기
- 다음 명령어를 실행하여
NetworkManager
의Metered
속성을 가져옵니다.busctl get-property org.freedesktop.NetworkManager /org/freedesktop/NetworkManager org.freedesktop.NetworkManager Metered
- 출력은
u 1
과 같이 나타날 수 있습니다. 여기서u
는 D-Bus 타입 서명으로, '부호 없는 32비트 정수(uint32)'를 의미합니다.NetworkManager
는 이 값을 통해 데이터 사용량에 제한이 있는 종량제 적용 여부를 나타냅니다 (예: 1은 'yes', 2는 'no').실행 결과
u 1
- 다음 명령어를 실행하여
busctl set-property <서비스> <객체> <인터페이스> <속성> <서명> <값>
: 값을 변경합니다. 이 명령은 강력하지만, 시스템에 직접적인 영향을 주므로 주의해서 사용해야 합니다.예제: systemd 로그 레벨을 debug로 설정하기
# systemd의 LogLevel 속성을 문자열(s) 'debug'로 설정 sudo busctl set-property org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager LogLevel s debug
이 예제는
systemd
서비스(org.freedesktop.systemd1
)의 메인 객체(/org/freedesktop/systemd1
)에 있는Manager
인터페이스의LogLevel
속성을 문자열(s
)debug
로 설정합니다. 여기서s
라는 서명은 D-Bus의 타입 안정성을 보장하는 핵심 요소입니다. 이는busctl
이 덜 구조화된 IPC 메커니즘에서 흔히 발생하는 데이터 타입 오류를 방지하는 방법입니다. D-Bus 명세 자체가 엄격한 타입 시스템을 기반으로 하며,busctl
은 이 명세를 구문으로 강제하여 견고함을 확보합니다.
실행 - 원격으로 메서드 호출하기
busctl
의 정점은 다른 프로세스의 함수를 직접 실행하는 것입니다.
busctl call <서비스> <객체> <인터페이스> <메서드> [서명][인자...]
: 원격 메서드를 호출합니다.예제:
busctl
로 서비스 재시작하기systemctl
의 대안으로busctl
을 사용하여 서비스를 재시작할 수 있습니다.# systemd의 RestartUnit 메서드를 호출하여 httpd 서비스를 재시작 sudo busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager RestartUnit ss "httpd.service" "replace"
이 명령은
systemd
의Manager
인터페이스에 있는RestartUnit
메서드를 호출합니다. 인자 서명ss
는 두 개의 문자열을 의미하며, 각각 서비스 이름인"httpd.service"
와 재시작 모드인"replace"
에 해당합니다.
고급 busctl
운영: 스크립팅, 디버깅, 그리고 보안
기본을 마스터한 시니어 엔지니어라면 이제 busctl
을 활용하여 더 복잡한 작업을 수행할 준비가 되었습니다.
busctl monitor
를 이용한 실시간 디버깅
busctl monitor
는 D-Bus를 위한 tcpdump
나 wireshark
와 같습니다. 버스를 가로지르는 모든 메시지를 실시간으로 엿들을 수 있어 시스템 동작을 이해하는 데 매우 강력한 도구입니다.
# 사용자의 로그인, 화면 잠금 등과 관련된 모든 시그널 보기
busctl monitor org.freedesktop.login1
하지만 모든 메시지를 보는 것은 정보의 홍수에 빠지기 쉽습니다. 이 때 강력한 --match
옵션을 사용하여 특정 시그널만 필터링할 수 있습니다.
# 모든 속성 변경(PropertiesChanged) 시그널만 모니터링
busctl monitor --match "type='signal',member='PropertiesChanged',interface='org.freedesktop.DBus.Properties'"
introspect
와 monitor
의 조합은 강력한 디버깅 루프를 만듭니다. 예를 들어, USB 드라이브를 분리했을 때 시스템에서 어떤 일이 일어나는지 알고 싶다고 가정해 봅시다. 먼저 busctl tree org.freedesktop.UDisks2
로 서비스 구조를 파악하고, busctl introspect
로 관련 객체와 인터페이스를 살펴 BlockDeviceRemoved
와 같은 시그널을 찾아냅니다. 그 다음 busctl monitor --match "member='BlockDeviceRemoved'"
를 실행하고 USB를 분리하면, 정확한 시그널이 출력되는 것을 볼 수 있습니다. 이 체계적인 과정은 복잡한 시스템 서비스의 동작을 역공학하는 핵심 기술입니다.
busctl
을 활용한 자동화 및 스크립팅
busctl
은 셸 스크립트에서 사용하여 견고한 자동화를 구축할 수 있습니다. systemctl
의 출력을 grep
이나 awk
로 파싱하는 것보다 busctl
을 사용하는 것이 훨씬 안정적입니다. busctl
은 컴퓨터가 파싱하기 쉬운 형식의 데이터를 제공하므로, 시스템 업데이트로 인해 출력 텍스트가 변경되어도 스크립트가 깨질 확률이 훨씬 낮습니다.
스크립트 예제: 주요 서비스 상태 점검
다음은 주요 서비스 목록을 순회하며 상태가 'active'가 아닌 경우 보고하는 간단한 셸 스크립트입니다.
#!/bin/bash
CRITICAL_SERVICES=("httpd.service" "sshd.service" "mariadb.service")
FAILED_SERVICES=()
echo "Critical service status check at $(date)"
for SERVICE in "${CRITICAL_SERVICES[@]}"; do
# systemd의 GetUnit 메서드를 호출하여 서비스의 객체 경로를 얻음
# 에러 출력을 /dev/null로 보내 존재하지 않는 서비스에 대한 오류를 숨김
OBJECT_PATH=$(busctl call org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.systemd1.Manager GetUnit s "$SERVICE" 2>/dev/null | awk '{print $2}')
if; then
echo "WARNING: Could not find object path for $SERVICE. Is it installed?"
continue
fi
# 얻은 객체 경로를 사용하여 ActiveState 속성을 가져옴
ACTIVE_STATE=$(busctl get-property org.freedesktop.systemd1 "$OBJECT_PATH" org.freedesktop.systemd1.Unit ActiveState | awk -F'"' '{print $2}')
if; then
echo "ALERT: Service $SERVICE is not active. Current state: $ACTIVE_STATE"
FAILED_SERVICES+=("$SERVICE")
else
echo "OK: Service $SERVICE is active."
fi
done
if} -ne 0 ]; then
echo "Summary: The following services are not running: ${FAILED_SERVICES[*]}"
# 여기에 알림 로직 추가 (예: 이메일 전송)
exit 1
fi
echo "All critical services are running correctly."
exit 0
이 스크립트는 GetUnit
메서드를 호출하여 각 서비스의 동적 객체 경로를 안정적으로 찾은 다음, get-property
를 사용하여 상태를 확인합니다. 이는 systemctl is-active
보다 더 상세한 정보를 제공하며, D-Bus API를 직접 활용하는 훨씬 견고한 방법입니다.
"Permission Denied" 미스터리: PolicyKit(Polkit) 이해하기
busctl
을 사용하다 보면 가장 흔하게 마주치는 좌절스러운 문제가 바로 "Permission Denied" 또는 "Access Denied" 오류입니다. 많은 관리자들이 sudo
를 붙여 이 문제를 회피하지만, 이는 근본적인 원인을 이해하지 못한 것입니다.
이 오류의 진실은 busctl
자체에 있는 것이 아니라, D-Bus를 통해 요청을 받은 서비스(예: logind
)와 PolicyKit(polkitd
) 데몬 사이의 상호작용에 있습니다. 전체 과정은 다음과 같습니다.
- 사용자가
busctl
명령을 실행합니다 (예: 시스템 재부팅). busctl
은 해당 요청을 D-Bus 메시지로 만들어 시스템 버스에 보냅니다.systemd-logind
서비스가 메시지를 수신합니다.logind
서비스는 스스로 결정을 내리지 않고, 권한 관리자인polkitd
에게 묻습니다: "사용자 'john'이 'org.freedesktop.login1.reboot' 액션을 수행할 수 있습니까?".polkitd
는/etc/polkit-1/rules.d
와/usr/share/polkit-1/rules.d
에 있는 규칙 파일들을 확인합니다.- 규칙에 따라
polkitd
는 "아니요, 허용되지 않습니다"라고 응답합니다. logind
서비스는 이 응답을 받아 "Access Denied" 오류를 D-Bus를 통해 되돌려 보냅니다.busctl
은 이 오류 메시지를 사용자에게 출력합니다.
따라서 문제의 근원은 busctl
이 아니라, polkit
정책입니다. 올바른 해결책은 sudo
를 남용하는 것이 아니라, 특정 사용자나 그룹에게 해당 작업을 수행할 수 있는 명시적인 권한을 부여하는 .rules
파일을 작성하는 것입니다. 이는 훨씬 더 안전하고 정교한 시스템 관리 방식입니다.
아키텍트의 관점: 엔터프라이즈 생태계 속 D-Bus와 busctl
이제 기술적인 '어떻게'를 넘어 전략적인 '왜'를 살펴보겠습니다. 이 섹션은 관리자, 아키텍트, CTO를 위해 D-Bus와 busctl
이 갖는 더 큰 의미를 조명합니다.
현대 데이터센터를 위한 통합 API
D-Bus는 운영체제에 표준화되고, 스스로를 설명하며(introspectable), API 기반인 제어 평면을 제공합니다. 이는 엔터프라이즈 환경에서 엄청난 전략적 가치를 가집니다.
- 신뢰성:
busctl
API를 사용하는 자동화 스크립트는 텍스트 출력을 파싱하는 스크립트보다 시스템 업데이트 시 깨질 가능성이 훨씬 낮습니다. 이는 유지보수 비용을 절감하고 시스템 안정성을 높입니다. - 관측 가능성(Observability): 공통 버스를 통해 서비스 간의 상호작용을 중앙에서 모니터링할 수 있어, 문제의 근본 원인을 더 빠르고 깊이 있게 파악할 수 있습니다.
- 보안:
polkit
을 통한 중앙 집중식 권한 부여는 광범위한sudo
권한을 부여하는 것보다 훨씬 세분화된 제어를 가능하게 하여 '최소 권한 원칙'을 구현하는 데 도움이 됩니다. - 개발자 효율성: 개발자들은 일관되고 잘 문서화된 메커니즘을 사용하여 시스템 서비스와 상호작용할 수 있으므로, 시스템 수준의 도구를 개발하는 데 드는 시간과 노력이 줄어듭니다.
성능, 사소한 문제인가?
D-Bus의 성능에 대한 논의가 종종 있습니다. 레퍼런스 구현인 dbus-daemon
이 특정 고부하 시나리오에서 병목 현상을 일으킬 수 있다는 것은 사실입니다. 하지만 일반적인 관리 작업에서는 성능이 문제가 되는 경우는 거의 없습니다.
중요한 점은 D-Bus 생태계가 성숙하여 dbus-broker
와 같은 고성능 대체 구현이 존재한다는 것입니다. 이는 D-Bus가 현대적인 요구에 맞춰 계속 발전하고 있음을 보여주며, 아키텍트가 특정 사용 사례에 맞는 최적의 구성을 선택할 수 있음을 의미합니다.
자주 묻는 질문 (FAQ)
Q1 (초심자): systemctl
과 busctl
의 진짜 차이점은 무엇인가요?
A: 자동차에 비유해 봅시다. systemctl
은 운전석의 대시보드입니다. 시동을 걸고 끄고, 엔진 상태를 확인하는 쉬운 버튼(systemctl start httpd
)을 제공합니다. 반면 busctl
은 정비사가 연결하는 진단 포트(OBD-II)와 같습니다. 이를 통해 엔진 제어 장치(ECU)와 직접 대화하고, 특정 센서 값을 읽고, 저수준 명령을 내릴 수 있습니다. 사실, systemctl
도 내부적으로는 D-Bus를 통해 busctl
과 동일한 메커니즘을 사용하여 작업을 수행합니다.
Q2 (초심자/중급자): 서비스 이름이나 객체 경로가 왜 이렇게 길고 복잡한가요?
A: 자바 패키지 이름처럼 역방향 도메인 이름 규칙을 따르기 때문입니다. 이는 서로 다른 소프트웨어 공급업체 간의 이름 충돌을 방지하기 위한 것입니다. 예를 들어, org.kde.foo
와 org.gnome.foo
가 문제없이 공존할 수 있도록 보장합니다. 길고 복잡해 보이지만, 그 덕분에 이름이 명확하고 고유해집니다.
Q3 (전문가): busctl call
로 a{sv}
(문자열-가변형 딕셔너리)와 같은 복잡한 데이터 타입을 어떻게 전달하나요?
A: 전체 서명과 함께 인자를 평탄화된 형식으로 제공해야 합니다. 예를 들어, {'Key1': 'Value1'}
이라는 항목 하나를 가진 a{sv}
딕셔너리를 전달하려면 명령은 다음과 같을 것입니다: busctl call......... MethodName "a{sv}" 1 "Key1" s "Value1"
. 여기서 1
은 딕셔너리 항목의 개수이고, s "Value1"
은 variant 부분(서명 s
, 값 "Value1"
)을 나타냅니다. 이처럼 명시적인 형식은 D-Bus 명세에 의해 요구되는 사항입니다.
Q4 (전문가): busctl
을 사용하여 원격 머신이나 컨테이너의 서비스를 관리할 수 있나요?
A: 예, 가능합니다. busctl
은 SSH를 통한 원격 관리를 위한 --host
플래그와 로컬 systemd 컨테이너와 상호작용하기 위한 --machine
플래그를 제공합니다. 예를 들어, busctl --host=user@remoteserver list
는 원격 머신의 서비스 목록을 보여줍니다. 이는 중앙 집중식 관리를 위한 매우 강력한 기능입니다.
busctl
은 단순한 명령어가 아닙니다. 그것은 현대 리눅스 시스템의 복잡성을 이해하고 제어하기 위한 열쇠입니다. 이 가이드를 통해 여러분은 Rocky Linux 9 시스템의 숨겨진 힘을 발견하고, 더 효율적이고, 더 안정적이며, 더 안전한 시스템을 구축하는 능력을 갖추게 되었을 것입니다. 이제 직접 터미널을 열고 시스템과의 대화를 시작해 보십시오.
- 블로그 : www.infracody.com
이 글이 유익했나요? 댓글로 소중한 의견을 남겨주시거나 커피 한 잔의 선물은 큰 힘이 됩니다.