Ubuntu 22.04 Server에서 USB 스피커 자동 설정이 재부팅 후 실패할 때 해결 방법

1. 문제 상황

TurtleBot3 또는 Ubuntu 22.04 Server 환경에서 USB 스피커를 기본 출력 장치로 설정하기 위해 다음 명령어를 사용했습니다.

pactl set-default-sink alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

수동으로 실행하면 정상적으로 동작합니다.

하지만 이 명령어를 자동 실행 스크립트나 systemd 서비스에 등록한 뒤 전원을 끄고 다시 부팅하면 USB 스피커가 기본 출력 장치로 설정되지 않는 문제가 발생했습니다.

증상은 다음과 같습니다.

- 수동 실행하면 정상 동작
- 재부팅 후에는 자동 적용 실패
- pactl list short sinks를 나중에 확인하면 USB 스피커는 보임
- 하지만 기본 출력은 USB 스피커가 아님

이 문제의 핵심 원인은 대부분 다음과 같습니다.

자동 실행 스크립트가 먼저 실행되고,
USB 오디오 장치 또는 PulseAudio sink 등록은 나중에 완료됨

즉, 부팅 순서 문제입니다.

2. 문제 발생 원인

Ubuntu Server나 TurtleBot3 같은 임베디드/로봇 환경에서는 부팅 직후 모든 장치가 동시에 준비되지 않습니다.

특히 USB 스피커는 다음 순서로 준비됩니다.

1. 리눅스 커널 부팅
2. USB 장치 인식
3. ALSA 오디오 장치 등록
4. PulseAudio 실행
5. PulseAudio가 ALSA 장치를 sink로 등록
6. pactl에서 USB 스피커 sink 확인 가능

그런데 systemd 자동 실행 서비스가 2번이나 3번 시점에 먼저 실행되면 다음 명령어가 실패합니다.

pactl set-default-sink alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

왜냐하면 그 순간에는 아직 해당 sink가 존재하지 않기 때문입니다.

3. 해결 방법

이번 글에서는 다음 방식으로 문제를 해결합니다.

1. USB 스피커 sink가 나타날 때까지 계속 확인
2. sink가 보이면 기본 출력 장치로 설정
3. 기본 출력이 다른 장치로 바뀌면 다시 USB 스피커로 복구
4. USB 스피커를 늦게 인식해도 자동 설정
5. USB 스피커를 뽑았다가 다시 꽂아도 자동 복구
6. systemd user service로 부팅 후 자동 실행

즉, 한 번만 실행하고 끝나는 방식이 아니라 백그라운드에서 계속 감시하는 방식입니다.

4. 현재 USB 스피커 sink 이름 확인

먼저 현재 PulseAudio sink 목록을 확인합니다.

pactl list short sinks

예시 출력:

0 alsa_output.platform-bcm2835_audio.stereo-fallback
1 alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

여기서 USB 스피커 sink 이름은 다음입니다.

alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

이 글에서는 이 이름을 기준으로 설정합니다.

각자의 장비에서는 sink 이름이 다를 수 있으므로 반드시 본인 장비에서 나온 이름으로 바꿔야 합니다.

5. USB 스피커 수동 설정 테스트

먼저 수동 명령어가 정상 동작하는지 확인합니다.

pactl set-default-sink alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

설정 확인:

pactl info | grep "Default Sink"

정상 출력:

Default Sink: alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

스피커 테스트:

speaker-test -c 2 -t wav

또는:

aplay /usr/share/sounds/alsa/Front_Center.wav

6. 기존 자동 설정 스크립트 백업

기존에 만든 스크립트가 있다면 먼저 백업합니다.

mkdir -p ~/bin
mv ~/bin/set_usb_speaker.sh ~/bin/set_usb_speaker.sh.bak

7. watchdog 방식 자동 설정 스크립트 작성

새로운 스크립트를 작성합니다.

nano ~/bin/usb_speaker_watchdog.sh

아래 내용을 넣습니다.

#!/bin/bash

SINK_NAME="alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo"
CHECK_INTERVAL=5

echo "[USB Speaker Watchdog] Started"
echo "[USB Speaker Watchdog] Target sink: $SINK_NAME"

while true
do
    # PulseAudio가 아직 준비되지 않은 경우
    if ! pactl info >/dev/null 2>&1; then
        echo "[USB Speaker Watchdog] PulseAudio is not ready. Retry..."
        sleep "$CHECK_INTERVAL"
        continue
    fi

    # USB 스피커 sink가 아직 등록되지 않은 경우
    if ! pactl list short sinks | grep -q "$SINK_NAME"; then
        echo "[USB Speaker Watchdog] USB speaker sink not found. Retry..."
        sleep "$CHECK_INTERVAL"
        continue
    fi

    # 현재 기본 sink 확인
    CURRENT_SINK=$(pactl info | grep "Default Sink" | awk -F': ' '{print $2}')

    # 기본 sink가 USB 스피커가 아니면 변경
    if [ "$CURRENT_SINK" != "$SINK_NAME" ]; then
        echo "[USB Speaker Watchdog] Current sink: $CURRENT_SINK"
        echo "[USB Speaker Watchdog] Setting default sink to USB speaker..."

        pactl set-default-sink "$SINK_NAME"

        if [ $? -eq 0 ]; then
            echo "[USB Speaker Watchdog] Default sink changed to: $SINK_NAME"
        else
            echo "[USB Speaker Watchdog] Failed to set default sink"
        fi
    else
        echo "[USB Speaker Watchdog] USB speaker is already default"
    fi

    sleep "$CHECK_INTERVAL"
done

소스에 대한 설명입니다.

1) Shebang

#!/bin/bash

이 줄은 이 파일을 bash 셸로 실행하라는 의미입니다.

예를 들어 이 파일 이름이 usb_speaker_watchdog.sh라면:

./usb_speaker_watchdog.sh

로 실행했을 때 시스템은 첫 줄을 보고 /bin/bash를 사용해서 스크립트를 실행합니다.

이 줄이 없으면 환경에 따라 sh로 실행될 수 있고, 일부 bash 문법이 예상대로 동작하지 않을 수 있습니다.

2) USB 스피커 sink 이름 설정

SINK_NAME="alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo"

이 변수는 기본 출력 장치로 설정할 USB 스피커의 PulseAudio sink 이름입니다.

사용자가 수동으로 성공한 명령어가 이것이었습니다.

pactl set-default-sink alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

여기서 뒤쪽 긴 이름이 바로 sink 이름입니다.

즉, 아래 두 명령어에서 같은 값을 사용합니다.

pactl list short sinks
pactl set-default-sink "$SINK_NAME"

중요한 점은 이 sink 이름이 장비마다 다를 수 있다는 것입니다.

다른 USB 스피커를 쓰거나 USB 포트가 바뀌거나 장치 ID가 바뀌면 sink 이름도 달라질 수 있습니다.

현재 장비에서는 이 값이 맞습니다.

alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

3) 확인 주기 설정

CHECK_INTERVAL=5

이 값은 스크립트가 몇 초마다 상태를 다시 확인할지 정합니다.

현재는 5초입니다.

즉, 스크립트는 다음 작업을 5초마다 반복합니다.

- PulseAudio 준비 여부 확인
- USB 스피커 sink 등록 여부 확인
- 현재 기본 출력 장치 확인
- 필요하면 USB 스피커로 변경

5초는 로봇 시스템에서 적당한 값입니다.

너무 짧게 하면 로그가 많이 쌓이고, 너무 길게 하면 USB 스피커 복구가 늦어집니다.

추천값은 보통 이 정도입니다.

CHECK_INTERVAL=5

더 빠르게 반응시키고 싶으면:

CHECK_INTERVAL=2

로그를 줄이고 싶으면:

CHECK_INTERVAL=10

정도로 바꿀 수 있습니다.

4) 시작 로그 출력

echo "[USB Speaker Watchdog] Started"
echo "[USB Speaker Watchdog] Target sink: $SINK_NAME"

스크립트가 시작되면 로그를 출력합니다.

직접 실행하면 터미널에 보이고, systemd 서비스로 실행하면 journal 로그에 남습니다.

확인 명령어:

journalctl --user -u usb-speaker-watchdog.service -f

출력 예:

[USB Speaker Watchdog] Started
[USB Speaker Watchdog] Target sink: alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

이 로그를 보면 스크립트가 정상적으로 시작되었는지 확인할 수 있습니다.

5) 무한 반복 시작

while true
do

이 부분은 무한 반복문입니다.

true는 항상 참이므로, 이 반복문은 사용자가 중지하거나 systemd가 서비스를 종료하기 전까지 계속 실행됩니다.

즉, 이 스크립트는 일회성 설정 스크립트가 아닙니다.

계속 실행되면서 USB 스피커 상태를 감시하는 watchdog

입니다.

중지하려면 직접 실행 중일 때는:

Ctrl + C

systemd 서비스로 실행 중이면:

systemctl --user stop usb-speaker-watchdog.service

를 사용합니다.

6) PulseAudio 준비 여부 확인

if ! pactl info >/dev/null 2>&1; then
echo "[USB Speaker Watchdog] PulseAudio is not ready. Retry..."
sleep "$CHECK_INTERVAL"
continue
fi

이 부분은 PulseAudio가 현재 사용 가능한 상태인지 확인합니다.

핵심 명령어는 이것입니다.

pactl info

pactl info가 정상 동작하면 PulseAudio와 통신할 수 있다는 뜻입니다.

반대로 부팅 직후에는 이런 문제가 생길 수 있습니다.

- PulseAudio가 아직 실행되지 않음
- user session이 아직 준비되지 않음
- /run/user/1000/pulse/native 소켓이 아직 없음
- pactl이 PulseAudio 서버에 연결하지 못함

이때 pactl set-default-sink를 바로 실행하면 실패합니다.

그래서 이 스크립트는 먼저 pactl info로 PulseAudio 상태를 확인합니다.

>/dev/null 2>&1의 의미

pactl info >/dev/null 2>&1

이 부분은 출력 메시지를 버리는 코드입니다.

분해하면 다음과 같습니다.

>/dev/null   : 정상 출력(stdout)을 화면에 표시하지 않고 버림
2>&1 : 에러 출력(stderr)도 정상 출력과 같은 곳으로 보냄

즉, pactl info의 출력 내용은 필요 없고, 성공했는지 실패했는지만 판단하겠다는 뜻입니다.

!의 의미

if ! pactl info >/dev/null 2>&1; then

!는 결과를 반대로 뒤집습니다.

즉:

pactl info 성공 → if 조건 false
pactl info 실패 → if 조건 true

따라서 이 코드는 이렇게 해석하면 됩니다.

만약 PulseAudio와 통신할 수 없다면:
로그 출력
5초 대기
이번 반복은 건너뛰고 다시 처음부터 시작

continue의 의미

continue

continue는 현재 반복을 여기서 멈추고 다음 반복으로 넘어가라는 뜻입니다.

즉, PulseAudio가 준비되지 않은 상태에서는 아래 작업을 하지 않습니다.

- sink 목록 확인
- 기본 출력 장치 확인
- 기본 sink 변경

이 판단은 맞습니다. PulseAudio가 없으면 아래 명령어들이 어차피 실패하기 때문입니다.

7) USB 스피커 sink 등록 여부 확인

if ! pactl list short sinks | grep -q "$SINK_NAME"; then
echo "[USB Speaker Watchdog] USB speaker sink not found. Retry..."
sleep "$CHECK_INTERVAL"
continue
fi

이 부분은 USB 스피커가 PulseAudio sink 목록에 등록되었는지 확인합니다.

핵심 명령어는 이것입니다.

pactl list short sinks

예상 출력:

0	alsa_output.platform-bcm2835_audio.stereo-fallback	module-alsa-card.c	s16le 2ch 44100Hz	SUSPENDED
1 alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDED

여기서 grep으로 USB 스피커 이름이 있는지 찾습니다.

grep -q "$SINK_NAME"

grep -q의 의미

grep -q는 quiet 모드입니다.

즉, 찾은 내용을 화면에 출력하지 않고, 찾았는지 여부만 반환합니다.

찾음     → 성공 코드 0
못 찾음 → 실패 코드 1

따라서 이 코드는 이렇게 해석됩니다.

PulseAudio sink 목록 안에 USB 스피커 이름이 없으면:
아직 USB 스피커가 준비되지 않은 것
5초 기다렸다가 다시 확인

이 부분이 바로 부팅 타이밍 문제를 해결하는 핵심입니다.

8) 현재 기본 출력 장치 확인

CURRENT_SINK=$(pactl info | grep "Default Sink" | awk -F': ' '{print $2}')

이 줄은 현재 PulseAudio의 기본 출력 장치를 가져옵니다.

명령어를 나눠 보면 이렇습니다.

pactl info

예상 출력 일부:

Server String: /run/user/1000/pulse/native
Library Protocol Version: 35
Server Protocol Version: 35
Is Local: yes
Client Index: 34
Tile Size: 65472
User Name: ubuntu
Host Name: ubuntu
Server Name: pulseaudio
Default Sink: alsa_output.platform-bcm2835_audio.stereo-fallback
Default Source: alsa_input.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.mono-fallback

여기서 Default Sink 줄만 뽑습니다.

grep "Default Sink"

결과:

Default Sink: alsa_output.platform-bcm2835_audio.stereo-fallback

그 다음 awk: 뒤쪽 값만 뽑습니다. -F’:’는 필드 구분자입니다. $2는 두번째 값을 의미합니다.

awk -F': ' '{print $2}'

최종 결과:

alsa_output.platform-bcm2835_audio.stereo-fallback

이 값을 CURRENT_SINK 변수에 저장합니다.

즉, 이 줄의 최종 의미는 다음과 같습니다.

현재 기본 출력 장치 이름을 CURRENT_SINK 변수에 저장한다.

9) 현재 기본 출력이 USB 스피커인지 비교

if [ "$CURRENT_SINK" != "$SINK_NAME" ]; then

이 코드는 현재 기본 출력 장치가 원하는 USB 스피커인지 비교합니다.

CURRENT_SINK : 현재 기본 출력 장치
SINK_NAME : 우리가 원하는 USB 스피커

예를 들어 현재 상태가 이렇다면:

CURRENT_SINK=alsa_output.platform-bcm2835_audio.stereo-fallback
SINK_NAME=alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

두 값이 다르므로 조건은 참입니다.

이 경우 USB 스피커가 기본 출력이 아니므로 변경해야 합니다.

반대로 현재 상태가 이렇다면:

CURRENT_SINK=alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo
SINK_NAME=alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

두 값이 같으므로 변경할 필요가 없습니다.

10) 기본 출력 장치 변경

echo "[USB Speaker Watchdog] Current sink: $CURRENT_SINK"
echo "[USB Speaker Watchdog] Setting default sink to USB speaker..."

pactl set-default-sink "$SINK_NAME"

현재 기본 출력 장치를 로그로 출력한 뒤, 원하는 USB 스피커로 기본 출력을 변경합니다.

핵심 명령어는 이것입니다.

pactl set-default-sink "$SINK_NAME"

실제로 실행되는 명령어는 다음과 같습니다.

pactl set-default-sink alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

이 명령어가 성공하면 이후 aplay, mpg123, paplay, 일부 ROS 2 음성 출력이 USB 스피커로 나가게 됩니다.

11) 명령어 성공 여부 확인

if [ $? -eq 0 ]; then
echo "[USB Speaker Watchdog] Default sink changed to: $SINK_NAME"
else
echo "[USB Speaker Watchdog] Failed to set default sink"
fi

$?는 바로 직전에 실행한 명령어의 종료 코드를 의미합니다.

여기서는 바로 직전에 실행한 명령어가 이것입니다.

pactl set-default-sink "$SINK_NAME"

종료 코드 의미는 보통 다음과 같습니다.

0     : 성공
0 아님 : 실패

따라서:

if [ $? -eq 0 ]; then

은 이렇게 해석됩니다.

방금 pactl set-default-sink 명령이 성공했다면

성공하면:

[USB Speaker Watchdog] Default sink changed to: ...

실패하면:

[USB Speaker Watchdog] Failed to set default sink

를 출력합니다.

12) 이미 USB 스피커가 기본 출력인 경우

else
echo "[USB Speaker Watchdog] USB speaker is already default"
fi

현재 기본 출력 장치가 이미 USB 스피커라면 굳이 다시 설정하지 않습니다.

즉, 다음 상태라면:

CURRENT_SINK == SINK_NAME

아래 로그만 출력합니다.

[USB Speaker Watchdog] USB speaker is already default

13) 5초 대기 후 반복

sleep "$CHECK_INTERVAL"
done

모든 확인이 끝나면 5초 대기합니다.

그 후 다시 while true의 처음으로 돌아갑니다.

즉, 이 스크립트는 다음 작업을 계속 반복합니다.

5초마다:
PulseAudio 살아있나?
USB 스피커 sink 있나?
기본 출력이 USB 스피커인가?
아니면 USB 스피커로 바꿔라.

저장 후 실행 권한을 줍니다.

chmod +x ~/bin/usb_speaker_watchdog.sh

14) watchdog 스크립트 수동 테스트

먼저 터미널에서 직접 실행해 봅니다.

~/bin/usb_speaker_watchdog.sh

정상이라면 다음과 비슷한 로그가 나옵니다.

[USB Speaker Watchdog] Started
[USB Speaker Watchdog] Target sink: alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo
[USB Speaker Watchdog] USB speaker is already default

다른 장치가 기본 출력으로 잡혀 있으면 다음처럼 바뀝니다.

[USB Speaker Watchdog] Current sink: alsa_output.platform-bcm2835_audio.stereo-fallback
[USB Speaker Watchdog] Setting default sink to USB speaker...
[USB Speaker Watchdog] Default sink changed to: alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

테스트를 중지하려면 Ctrl + C를 누릅니다.

15) 기존 oneshot 서비스 비활성화

기존에 usb-speaker-default.service 같은 서비스를 만들었다면 비활성화합니다.

systemctl --user stop usb-speaker-default.service
systemctl --user disable usb-speaker-default.service

파일이 없어도 괜찮습니다. 에러가 나오면 무시해도 됩니다.

16) watchdog용 systemd user service 생성

이제 watchdog 스크립트를 systemd user service로 등록합니다.

mkdir -p ~/.config/systemd/user

서비스 파일을 만듭니다.

nano ~/.config/systemd/user/usb-speaker-watchdog.service

아래 내용을 넣습니다.

[Unit]
Description=USB Speaker PulseAudio Watchdog
After=default.target

[Service]
Type=simple
ExecStart=%h/bin/usb_speaker_watchdog.sh
Restart=always
RestartSec=3

[Install]
WantedBy=default.target

이 서비스는 한 번 실행하고 끝나는 구조가 아닙니다.
계속 실행되면서 USB 스피커 상태를 감시합니다.

17) systemd user 데몬 재로드

서비스 파일을 만든 뒤 user systemd 데몬을 다시 로드합니다.

systemctl --user daemon-reload

서비스를 활성화합니다.

systemctl --user enable usb-speaker-watchdog.service

바로 실행합니다.

systemctl --user start usb-speaker-watchdog.service

상태 확인:

systemctl --user status usb-speaker-watchdog.service

정상이라면 다음과 비슷하게 나옵니다.

Active: active (running)

18) SSH 로그인 없이 부팅 시 자동 실행되도록 설정

Ubuntu Server에서는 user service가 사용자가 로그인한 뒤에만 실행되는 경우가 있습니다.

로봇은 전원을 켜면 자동으로 동작해야 하므로, 로그인 없이도 user service가 실행되도록 linger를 설정합니다.

sudo loginctl enable-linger $USER

확인:

loginctl show-user $USER | grep Linger

정상 출력:

Linger=yes

이 설정이 되어 있어야 전원 인가 후 SSH 로그인 전에도 user service가 실행될 수 있습니다.

19) 재부팅 후 확인

이제 재부팅합니다.

sudo reboot

다시 접속한 뒤 서비스 상태를 확인합니다.

systemctl --user status usb-speaker-watchdog.service

정상이라면 다음 상태여야 합니다.

Active: active (running)

기본 출력 장치를 확인합니다.

pactl info | grep "Default Sink"

정상 출력:

Default Sink: alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

소리 테스트:

speaker-test -c 2 -t wav

또는:

aplay /usr/share/sounds/alsa/Front_Center.wav

20) 로그 확인 방법

watchdog 서비스가 실제로 어떻게 동작했는지 확인하려면 journal 로그를 봅니다.

journalctl --user -u usb-speaker-watchdog.service -f

예상 로그:

[USB Speaker Watchdog] Started
[USB Speaker Watchdog] Target sink: alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo
[USB Speaker Watchdog] PulseAudio is not ready. Retry...
[USB Speaker Watchdog] USB speaker sink not found. Retry...
[USB Speaker Watchdog] Setting default sink to USB speaker...
[USB Speaker Watchdog] Default sink changed to: alsa_output.usb-Jieli_Technology_UACDemoV1.0_4150353131373114-00.analog-stereo

이 로그가 보이면 부팅 초기에 PulseAudio나 USB 스피커가 늦게 잡히더라도 watchdog이 기다렸다가 정상적으로 설정한 것입니다.

21) USB 스피커를 뽑았다가 다시 꽂는 경우

이 방식의 장점은 USB 스피커를 뽑았다가 다시 꽂아도 자동 복구가 가능하다는 점입니다.

확인 절차는 다음과 같습니다.

1. USB 스피커 분리
2. 몇 초 대기
3. USB 스피커 다시 연결
4. watchdog이 sink를 다시 감지
5. 기본 출력 장치를 USB 스피커로 재설정

로그 확인:

journalctl --user -u usb-speaker-watchdog.service -f

22) 문제가 계속될 때 확인할 명령어

USB 스피커가 ALSA에서 보이는지 확인

aplay -L | grep -A3 UAC

또는:

cat /proc/asound/cards

PulseAudio sink로 보이는지 확인

pactl list short sinks

기본 출력 장치 확인

pactl info | grep "Default Sink"

watchdog 서비스 상태 확인

systemctl --user status usb-speaker-watchdog.service

watchdog 로그 확인

journalctl --user -u usb-speaker-watchdog.service -f

linger 설정 확인

loginctl show-user $USER | grep Linger

23) 기존 설정을 삭제하고 다시 시작하고 싶을 때

watchdog 서비스를 중지합니다.

systemctl --user stop usb-speaker-watchdog.service

자동 실행을 비활성화합니다.

systemctl --user disable usb-speaker-watchdog.service

서비스 파일을 삭제합니다.

rm ~/.config/systemd/user/usb-speaker-watchdog.service

스크립트 삭제:

rm ~/bin/usb_speaker_watchdog.sh

systemd user 데몬 재로드:

systemctl --user daemon-reload

Leave a Comment