소개
Maple Yard 방문자는 간단한 사진 미리보기나 사진 부스의 전체 앨범을 원합니다. 사진 서버는 이미 작동하지만, 방문자에게 IP 주소와 포트가 적힌 종이를 줄 필요는 없어야 합니다. 이름이 있는 서비스 2개를 게시하고, 클라이언트에서 검색하여 해당 이미지 모음을 저장하면서 안내 데스크를 유지합니다.
IPv4 주소, 호스트 이름, 포트, 경로, 터미널 역할을 구분할 수 있고 완전한 HTTP 다운로드 명령을 따라 실행한 경험이 필요합니다. 이전의 행사 네트워크 프로젝트를 먼저 학습하는 것을 권장합니다. DNS 관리, XML, 프로그래밍 경험은 필요하지 않습니다. 예상 60분에는 읽기와 일반적인 실수 처리가 포함됩니다. 이는 작성자의 추정이며 초보자를 대상으로 측정한 시간이 아닙니다.
Publisher, Client, New Client는 준비된 하나의 LAN에서 격리된 Linux 네트워크 노드이며 커널 하나를 공유합니다. Photo Shelf는 다운로드한 파일을 미리 보여 줍니다. 이 실습은 로컬 링크의 mDNS와 DNS-SD를 다룹니다. 공개 DNS, 라우터를 넘는 검색, 자동 권한 부여 또는 모든 실제 기기와 브라우저의 호환성을 다루지는 않습니다. 예제 이미지는 직접 만든 합성 일러스트 카드이며 실제 참석자의 사진이 아닙니다.
작동하는 서비스 검색하기
이 단계에서는 새 서비스를 게시하기 전에 정상 작동하는 안내 데스크를 사용합니다. Client에서 시작합니다. 터미널은 /home/labex/project/photo-network/client에서 시작하며 이 디렉터리와 셸은 다른 탭과 분리되어 있습니다. pwd는 현재 디렉터리를 출력합니다.
pwd
멀티캐스트 DNS(mDNS)는 이웃 기기가 같은 로컬 링크에서 이름 정보를 주고받게 합니다. 여기서는 IPv4 멀티캐스트 주소 224.0.0.251와 UDP 포트 5353를 사용합니다. .local 접미사는 이 로컬 검색 방식에 속합니다. 플랫폼의 hosts 파일에 추가하거나 공개 DNS 영역으로 취급하지 마세요.
DNS 기반 서비스 검색(DNS-SD)은 이름이 있는 서비스 인스턴스를 설명합니다. 서비스 유형은 클라이언트가 찾는 대상을 나타냅니다. _http._tcp는 TCP를 사용하는 HTTP를 뜻합니다. Information Desk 같은 인스턴스 이름은 제공되는 서비스 하나를 식별합니다. 대상 호스트 이름은 서비스를 제공하는 노드를 식별합니다. 포트는 수신 대기 애플리케이션을 선택하고, path 메타데이터는 그 안의 리소스를 선택합니다. 이름을 찾는 것은 서비스 이용의 시작일 뿐입니다.
avahi-browse는 기본 Avahi 데몬에 서비스 검색을 요청합니다. -r은 각 인스턴스를 접속 정보로 확인하고, -t는 검색이 끝나면 종료하며, -p는 세미콜론으로 구분한 필드를 출력합니다. -k는 읽기 쉬운 번역 이름 대신 원래 서비스 유형 이름을 유지합니다.
avahi-browse -rtpk _http._tcp
=로 시작하며 다음 필드를 포함하는 확인된 행이 나와야 합니다.
=;eth0;IPv4;Information\032Desk;_http._tcp;local;desk.local;10.93.0.10;8080;"path=/welcome/index.json"
새로 발견한 인스턴스에는 + 행이 출력될 수도 있습니다. 파싱 가능한 출력에서 \032는 공백을 나타냅니다. 확인된 행을 인스턴스, 유형, 도메인, 대상 호스트 이름, 주소, 포트, TXT 메타데이터 순서로 읽으세요. 행 순서와 이스케이프 표현은 달라질 수 있습니다. 기본 데몬은 이 LAN을 통해 실제 레코드를 받습니다. 미리 준비한 검색 목록은 없습니다.
제공된 photo-get 클라이언트로 인스턴스 이름을 지정해 모음을 저장합니다. 따옴표는 공백이 포함된 이름을 하나의 인자로 유지합니다. 두 번째 인자는 이 터미널 작업 공간 안의 출력 폴더입니다.
photo-get "Information Desk" desk
photo-get은 실제 Avahi 검색을 다시 수행하여 해당 인스턴스를 정확히 찾고 접속 정보를 출력합니다. 알림에 있는 JSON 인덱스와 여기에 나열된 각 PNG를 가져오고, 모든 이미지를 디코딩한 뒤 받은 바이트를 저장합니다. IP, 포트, 경로를 스테이션 계획에서 읽어 오지 않습니다. 인덱스는 이미지 파일 이름을 나열합니다. 이는 이 서비스의 애플리케이션 규약이며 일반적인 DNS-SD 형식은 아닙니다. Saved 1 real images가 표시되고 desk 폴더에 저장되어야 합니다.
Photo Shelf로 전환하고 Client를 선택한 뒤 저장 파일을 새로 고칩니다. Welcome to Maple Yard 카드가 보여야 합니다. 뷰어는 이 클라이언트 작업 공간에 저장된 파일만 읽으며 서버에서 가져오거나 누락된 이미지를 채우지 않습니다. Download image 링크는 같은 저장 파일을 반환합니다.

Client 선택은 현재 작업 공간을 나타냅니다. 안내 데스크 카드는 사진 부스를 추가하는 동안 유지해야 할 정상 작동 기준입니다.
검색 결과가 비어 있으면 정확한 서비스 유형과 현재 Client에 있는지 확인한 뒤 다시 검색합니다. Daemon not running은 검색 클라이언트가 준비된 데몬에 연결할 수 없다는 뜻이며 빈 결과와 다릅니다. photo-get이 이름을 찾지 못하면 확인된 행과 철자 및 따옴표를 비교하세요. 연결 거부는 주소를 찾았지만 해당 포트에서 연결을 받는 프로그램이 없다는 뜻입니다. HTTP 404는 서버에 도달했지만 리소스 경로가 없다는 뜻입니다. JSON 또는 이미지 디코딩 오류는 반환 내용이 적절하지 않다는 뜻입니다. hosts 항목이나 수동으로 복사한 사진으로 문제 해결을 대신하지 마세요.
완전한 템플릿으로 부스 게시하기
이 단계에서는 기존 미리보기 서버를 알립니다. Publisher로 전환합니다. 터미널은 /home/labex/project/photo-network/services에서 시작합니다. cat은 텍스트 파일을 출력하고 ../는 부모 디렉터리를 뜻합니다.
cat ../stations.tsv
이 업무 계획은 준비된 실제 수신 대기 서비스를 설명합니다. Photo Booth Preview는 publisher.local에 있으며 포트는 8081, 리소스는 /preview/index.json입니다. 전체 앨범은 다른 수신 대기 서비스와 리소스를 사용합니다. 안내 데스크는 별도 노드에 있으며 변경하면 안 됩니다.
미완성 미리보기 정의를 읽습니다.
cat preview.service
여기서 .service 파일은 XML을 사용합니다. 여는 태그와 닫는 태그가 각 값을 감쌉니다. service-group은 표시할 name을 포함하며, 그 안의 service는 type, port, txt-record를 지정합니다. 정의를 게시한다고 웹 서버, 모음 또는 접근 권한이 생성되지는 않습니다. 해당 애플리케이션은 이미 실행 중입니다.
다음 here-document는 작은 파일 전체를 씁니다. >는 기존 내용을 바꿉니다. 따옴표가 있는 'XML'은 텍스트를 그대로 유지하며 마지막 XML은 줄 시작에 단독으로 있어야 합니다. 전체 블록을 복사하세요.
cat > preview.service <<'XML'
<?xml version="1.0" standalone="no"?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name>Photo Booth Preview</name>
<service>
<type>_http._tcp</type>
<port>8081</port>
<txt-record>path=/preview/index.json</txt-record>
</service>
</service-group>
XML
저장한 정의를 읽습니다.
cat preview.service
강좌의 photo-load 명령은 편집 가능한 두 XML 파일을 파싱한 뒤 Publisher의 Avahi 데몬에만 다시 로드하도록 요청합니다. 안내 데스크나 업무 서버는 재시작하지 않습니다.
photo-load
Reload requested for the photo publisher가 나와야 합니다. 이름 확인과 등록을 위해 몇 초 기다립니다. 파싱 성공은 XML 구조가 유효하다는 뜻이지 알린 포트나 경로가 맞다는 증거는 아닙니다. 닫는 태그가 없으면 photo-load가 파일 이름과 파싱 오류를 보고하고 다시 로드 요청을 보내지 않습니다. 파일을 읽고 완전한 블록으로 복구한 뒤 photo-load를 다시 실행하세요. 환경을 초기화할 필요는 없습니다.
Client로 전환해 다시 검색합니다.
avahi-browse -rtpk _http._tcp
Photo Booth Preview와 대상 publisher.local, 포트 8081, /preview/index.json을 찾습니다. 실제 모음을 저장하고 디코딩합니다.
photo-get "Photo Booth Preview" preview
미리보기 이미지 1개가 나와야 합니다. Photo Shelf에서 Client를 선택하고 새로 고친 뒤 기존 안내 데스크 카드 옆에 있는 미리보기 카드를 살펴보세요. 목록에 서비스가 보이더라도 올바른 이미지를 얻지 못하면 아직 미완성입니다.
두 서비스 구분하기
이 단계에서는 같은 노드에 전체 앨범을 게시하고 두 인스턴스를 구분합니다. Publisher로 전환합니다. 둘 다 _http._tcp와 publisher.local을 사용하지만 앨범은 포트 8082와 /album/index.json을 사용합니다. 이미지 3개가 있으며 이미지 1개인 미리보기와 다릅니다.
앞서 배운 here-document 구문으로 별도 정의를 씁니다.
cat > album.service <<'XML'
<?xml version="1.0" standalone="no"?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
<name>Photo Booth Full Album</name>
<service>
<type>_http._tcp</type>
<port>8082</port>
<txt-record>path=/album/index.json</txt-record>
</service>
</service-group>
XML
photo-load
Client로 전환합니다. 검색한 뒤 확인된 행을 각각 읽습니다.
avahi-browse -rtpk _http._tcp
두 인스턴스 이름은 달라야 합니다. 같은 호스트 이름이라고 같은 포트나 리소스를 뜻하지는 않습니다. 미리보기를 유지하면서 앨범을 전용 폴더에 다운로드합니다.
photo-get "Photo Booth Full Album" album
PNG 3개가 디코딩되고 Saved 3 real images가 나와야 합니다. Photo Shelf에서 Client를 선택하고 새로 고칩니다. 앨범과 미리보기는 각각 자신이 저장한 파일을 보여야 합니다.
이제 올바른 모음을 수정하거나 지우지 않고 잘못된 리소스를 알아보는 연습을 합니다. 의도적으로 없는 인스턴스 이름을 사용하므로 다운로드 전에 실패해야 합니다.
photo-get "Photo Booth Missing" missing
Download stopped: Expected one resolved instance와 0이 아닌 종료 상태가 예상됩니다. 의도한 실패입니다. 실제로 확인된 앨범 이름을 다시 사용하여 복구합니다.
photo-get "Photo Booth Full Album" album
잘못된 포트나 경로를 알렸다면 해당 인스턴스의 확인된 행을 stations.tsv와 비교합니다. Publisher에서 해당 .service 파일만 제시된 완전한 블록으로 수정하고 photo-load를 실행한 뒤 Client에서 다시 검색하고 다운로드합니다. 잘못된 수신 포트는 연결을 거부할 수 있고 잘못된 경로는 HTTP 404를 반환할 수 있습니다. 접속 가능한 다른 모음은 정상 디코딩되어도 업무 결과로는 틀릴 수 있습니다. 종료 상태 0만 보지 말고 모음 제목과 파일 이름도 확인하세요.
Avahi는 변경과 철회를 알립니다. 업데이트가 전달되는 동안 클라이언트가 이전 레코드를 잠시 유지할 수 있습니다. 로드 후 몇 초 기다리고 다시 검색하세요. 복사한 옛 접속 정보를 계속 사용하지 마세요. 다음 단계는 독립된 클라이언트 데몬과 작업 공간에서 결과를 확인합니다.
다른 클라이언트에서 결과 사용하기
이 단계에서는 다른 네트워크 식별 정보와 독립된 Avahi 데몬을 사용합니다. New Client로 전환합니다. 시작 위치는 /home/labex/project/photo-network/new-client이며 기본적으로 첫 클라이언트의 셸 변수나 저장된 모음을 공유하지 않습니다. 탭을 열어도 게시 노드를 초기화하거나 서비스 알림을 생성하지 않습니다.
첫 클라이언트의 행을 복사하지 말고 이 클라이언트에서 검색합니다.
avahi-browse -rtpk _http._tcp
확인된 인스턴스 3개의 관계를 모두 읽습니다. 미리보기와 전체 앨범은 대상이 같지만 포트와 경로가 다릅니다. 안내 데스크에는 별도 대상이 있습니다. 이 클라이언트에서 세 모음을 모두 다운로드합니다.
photo-get "Photo Booth Preview" preview
photo-get "Photo Booth Full Album" album
photo-get "Information Desk" desk
미리보기 1개, 앨범 이미지 3개, 안내 데스크 카드 1개가 예상됩니다. 각 호출은 지정한 인스턴스를 다시 찾고 실제 사용한 접속 정보를 출력합니다. 이 명령은 정적 IP 검색을 설정하지 않습니다.
Photo Shelf로 전환해 New Client를 선택하고 새로 고칩니다. 모든 이미지와 모음 제목을 확인하세요. 저장된 이미지 카드의 Download image를 선택합니다. 다운로드한 이미지는 화면의 저장된 카드와 일치해야 합니다. 뷰어는 첫 클라이언트의 결과를 몰래 복사하거나 누락 파일을 채우지 않습니다. Client와 New Client를 전환하여 서로 분리된 모음을 확인하세요.

New Client 선택, 앨범 카드 3개, 안내 데스크 카드, 별도의 미리보기 카드를 찾으세요. 각 Download image 링크는 실제 저장 파일에 연결됩니다.
모음이 없으면 New Client로 돌아가 현재 서비스 알림을 확인한 뒤 해당 다운로드를 반복합니다. 저장된 PNG가 없거나 읽을 수 없으면 뷰어는 대체하지 않고 문제를 알립니다. 먼저 게시 정보나 연결을 바로잡고 photo-get을 다시 실행해 완전한 원본 모음을 가져오세요. 전체 인덱스와 모든 이미지를 가져와 디코딩해야 다운로드가 성공합니다.
이제 서비스 게시와 실제 사용을 연결했습니다. 구별 가능한 인스턴스 3개, 올바른 현재 접속 정보, 두 클라이언트의 실제 파일, 계속 사용할 수 있는 안내 데스크가 있습니다. 검색은 접속 정보를 제공할 뿐이며 인증이나 발견한 서비스에 대한 신뢰를 보장하지 않습니다.
요약
기본 mDNS/DNS-SD 레코드를 검색하고 인스턴스 이름과 호스트 이름을 구분했으며, 서로 다른 포트와 경로 메타데이터로 HTTP 서비스 2개를 게시했습니다. 각 실제 검색 결과를 사용해 디코딩 가능한 이미지를 다운로드하고 독립 클라이언트에서 흐름을 반복하면서 안내 데스크를 유지했습니다. 검색 행과 연결 성공은 서로 다른 증거입니다. 올바른 내용을 저장해야 결과가 완성됩니다.



