단축 URL은 긴 주소를 압축한 파일이 아니라, 짧은 주소와 원래 목적지를 연결한 별도의 웹 주소입니다. 짧은 링크를 누르면 서버가 이동할 곳을 알려주고 브라우저가 목적지를 다시 요청합니다. 이 구조를 이해하면 UTM이 사라지는 문제, 잘못된 페이지로 이동하는 문제, 클릭 수가 방문자 수와 다른 이유를 설명할 수 있습니다.
문자나 SNS에 링크를 전달하는 사람뿐 아니라 웹사이트와 자동화 스크립트를 만드는 개발자도 알아둘 만한 내용입니다. 이 글에서는 단축 URL의 동작 원리, Python으로 UTM 주소를 구성하는 예제, 실제 배포 전에 확인할 항목을 연결해서 살펴봅니다.
단축 URL 안에 원본 페이지가 저장되는 것은 아닙니다
일반적인 단축 서비스는 짧은 코드와 목적지 URL의 대응 관계를 저장합니다. 예를 들어 어떤 코드가 제품 안내 페이지에 연결되어 있으면, 그 코드로 들어온 요청을 해당 페이지로 보냅니다. 원본 페이지의 내용까지 복사하거나 계속 보존해주는 기능과는 다릅니다.
다음은 구조를 설명하기 위한 가상의 예시입니다. 실제로 생성한 단축 링크가 아닙니다.
- 운영자가
https://example.com/guide?lang=ko를 목적지로 등록합니다. - 서비스가 짧은 코드와 목적지를 연결합니다.
- 방문자가 짧은 주소를 요청하면 서버가 이동 응답을 돌려줍니다.
- 브라우저가 원래 목적지를 요청해 페이지를 표시합니다.
따라서 원본 페이지가 삭제되거나 단축 서비스가 연결을 중단하면 짧은 주소도 기대한 내용을 보여주지 못합니다. 장기간 배포할 링크라면 주소 길이뿐 아니라 목적지 유지와 만료 정책도 함께 고려해야 합니다.
302와 Location 헤더가 하는 일
서버가 보내는 응답에는 상태 코드와 헤더가 있습니다. 예를 들어 302 Found와 Location 헤더를 받으면 브라우저는 그 헤더에 담긴 주소로 이동할 수 있습니다. 아래 역시 설명용 응답입니다.
HTTP/1.1 302 Found
Location: https://example.com/guide?lang=ko
HTTP 표준 RFC 9110에서 302는 다른 URI에 임시로 위치한다는 의미입니다. 301·308은 영구 이동, 307은 요청 메서드를 유지하는 임시 이동을 나타냅니다. 301·302는 일부 상황에서 POST가 GET으로 바뀔 수 있으므로, 일반 링크 클릭과 폼·API 요청의 리다이렉트는 구분해서 설계해야 합니다.
단축 URL이 무조건 302를 사용해야 하는 것은 아닙니다. 목적지 변경 가능성, 캐시 정책, 서비스 설계에 따라 선택이 달라집니다. 특히 ‘301이면 검색 순위가 오른다’거나 ‘302이면 검색에 잡히지 않는다’고 단정하면 안 됩니다. Google의 리다이렉트 문서는 영구·임시 이동을 대표 URL 선택에 사용하는 신호로 설명합니다. 상태 코드만으로 순위 상승을 보장하지는 않습니다.
원본 URL, UTM, 단축 URL을 순서대로 준비합니다
UTM은 캠페인과 유입 경로를 구분하기 위해 목적지 URL에 붙이는 매개변수입니다. 단축 기능은 주소를 전달하기 편하게 만들고, UTM은 분석 도구가 유입을 분류할 단서를 전달합니다. 두 기능은 함께 쓸 수 있지만 같은 기능은 아닙니다.
| 값 | 구분하는 대상 | 이 글의 예시 |
|---|---|---|
| utm_source | 어디에서 배포했는가 | newsletter |
| utm_medium | 어떤 전달 방식인가 | |
| utm_campaign | 어떤 캠페인인가 | dev_guide_202609 |
Google Analytics의 캠페인 URL 안내는 이러한 매개변수를 통해 유입 캠페인을 구분하는 방법을 설명합니다. 값은 대소문자를 구분하므로 팀에서 소문자와 일정한 이름 규칙을 정해두는 편이 관리하기 쉽습니다. UTM을 붙이는 것만으로 통계 수집이 시작되지는 않으며, 목적지의 분석 도구가 해당 값을 읽고 기록해야 합니다.
작업 순서는 원본 페이지 확인 → 필요한 UTM 추가 → 완성한 주소를 단축 서비스에 등록 → 실제 이동 결과 확인으로 잡으면 됩니다. 단축 주소 뒤에 임의로 UTM을 덧붙이는 방법은 서비스가 그 값을 목적지로 전달하는지 확인하지 않으면 결과를 보장할 수 없습니다.
Python으로 기존 매개변수를 유지하며 UTM을 붙이기
주소 끝에 문자열을 무작정 붙이면 물음표가 두 번 들어가거나 # 뒤에 UTM이 붙는 실수가 생깁니다. 이미 있는 언어·필터 조건을 지우지 않으면서 URL의 각 부분을 나누어 조립하는 편이 명확합니다.
아래 예제는 직접 관리하는 일반 공개 페이지 주소를 대상으로 합니다. 기존 쿼리의 중복 항목과 빈 값을 유지하고, source·medium·campaign 세 항목만 교체합니다. 서명된 다운로드 URL처럼 문자열 자체에 검증 값이 묶인 주소에는 적용하지 마세요. 다시 인코딩하는 과정에서 원래 서명이 맞지 않을 수 있습니다.
from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
def add_utm(url, source, medium, campaign):
parts = urlsplit(url)
utm = {
"utm_source": source,
"utm_medium": medium,
"utm_campaign": campaign,
}
pairs = [
(key, value)
for key, value in parse_qsl(
parts.query, keep_blank_values=True
)
if key not in utm
]
pairs.extend(utm.items())
return urlunsplit((
parts.scheme, parts.netloc, parts.path,
urlencode(pairs), parts.fragment
))
result = add_utm(
"https://example.com/guide?lang=ko#start",
"newsletter", "email", "dev_guide_202609"
)
print(result)
실행 결과는 다음과 같습니다. 원래의 lang=ko와 #start가 남아 있고, UTM은 쿼리 부분에 들어갑니다.
https://example.com/guide?lang=ko&utm_source=newsletter&utm_medium=email&utm_campaign=dev_guide_202609#start
각 함수의 동작은 Python 공식 urllib.parse 문서에서 확인할 수 있습니다. 이 예제는 URL을 조립할 뿐 주소의 신뢰성이나 접근 가능성을 검증하지 않습니다. 특히 사용자 입력을 서버에서 열어주는 기능에 그대로 사용하면 안 되며, 그 경우에는 별도의 목적지 검증과 접근 제한이 필요합니다.
코드 없이 만들 때는 URLto에서 완성한 목적지를 등록합니다
한두 개의 링크를 공유하려는 목적이라면 직접 프로그램을 만들 필요는 없습니다. URLto의 URL 단축 기능에서 원본 주소를 입력해 짧은 링크를 만들 수 있습니다. 회원가입 없이 단축할 수 있고, 별칭 지정·QR 생성·별도 관리 주소를 통한 링크 관리 기능을 제공합니다.
UTM을 먼저 구성하고 싶다면 URLto UTM Builder에서 목적지와 source·medium·campaign을 입력하는 방식도 사용할 수 있습니다. 생성한 주소의 매개변수를 확인한 뒤 단축하세요. 주소가 짧아졌다는 사실과 분석 설정이 완료됐다는 사실은 따로 확인해야 합니다.
다른 사람에게는 공개용 짧은 주소를 전달하고, 별도의 관리용·통계용 주소는 공개 글이나 QR에 넣지 않는 것이 좋습니다. 관리 주소는 단순한 공유 링크와 역할이 다릅니다. 링크를 만들었다면 자신이 보관한 목적지와 최종 도착지를 한 번 대조해두세요.
‘열린다’만 확인하지 말고 세 가지를 나누어 검증합니다
개발자 도구의 Network 탭에서 로그 유지를 켠 뒤 자신이 관리하는 테스트 링크를 열면 중간 응답을 살펴볼 수 있습니다. 첫 이동의 상태 코드와 Location, 최종 주소, 화면의 실제 내용을 순서대로 확인해보세요. 시험 클릭도 집계에 들어갈 수 있으므로 검증 시각을 기록해두면 이후 해석에 도움이 됩니다.
| 검증 대상 | 통과했다고 볼 조건 | 실패 시 확인 |
|---|---|---|
| 연결 | 의도한 문서·상품·화면이 열린다 | 잘못된 목적지, 만료, 반복 이동 |
| 매개변수 | 필요한 언어·필터·UTM이 의도대로 전달된다 | 중간 리다이렉트의 쿼리 손실 |
| 측정 | 분석 도구에서 테스트 유입의 분류를 확인할 수 있다 | 태그 실행, 동의 설정, 수집 조건 |
최종 응답이 200이어도 화면이 로그인 페이지나 안내 페이지일 수 있습니다. 반대로 목적지의 프로그램이 UTM을 읽은 뒤 주소창에서 정리하는 경우도 있으므로, 매개변수가 나중에 안 보인다는 이유만으로 수집 실패를 확정하지는 마세요. 주소 전달과 실제 분석 결과를 함께 보는 이유입니다.
온라인 링크를 QR로 인쇄한다면 휴대전화에서 최종 페이지까지 확인해야 합니다. QR 그림이 인식되는 것과 목적지가 올바른 것은 별개의 점검입니다. 목적지를 바꿀 수 있는 구조인지, 만료되는 링크인지도 인쇄 전에 확인하세요.
클릭 100회가 방문자 100명을 뜻하지 않는 이유
단축 서비스의 클릭 집계는 링크 경유 단계에서 측정하고, 목적지의 페이지뷰나 사용자 수는 도착 이후의 별도 기준으로 측정합니다. 한 사람이 여러 번 눌러도 클릭은 늘 수 있고, 서비스의 필터에 따라 미리보기·자동 점검 요청이 포함될 수도 있습니다. 반대로 도착 후 분석 스크립트가 실행되지 않으면 목적지 통계에는 잡히지 않을 수 있습니다.
가령 링크 집계 100회와 목적지 페이지뷰 72회가 보인다고 해도 곧바로 ‘28명이 이탈했다’고 계산할 수는 없습니다. 이 숫자는 설명용 예시입니다. 집계 시간대, 중복 처리, 봇 제외, 페이지 로딩과 스크립트 실행 조건이 같아야 비교의 의미가 생깁니다.
처음에는 채널별 링크 이름과 배포 시각을 정리하고, 같은 기간의 집계 추세를 살펴보세요. 이후 가입이나 구매 같은 목표 행동은 목적지에서 별도로 확인해야 합니다. 클릭 수 하나만으로 콘텐츠의 만족도나 매출 효과를 판단하는 것은 피하는 편이 좋습니다.
단축 URL을 쓰면 좋은 곳과 원본 링크가 편한 곳
문자, 발표 자료, 인쇄물처럼 URL 자체를 보여주는 환경에서는 짧은 주소가 유용합니다. 반면 웹페이지 본문에서는 의미 있는 링크 문구에 원본 주소를 바로 연결할 수 있습니다. 같은 사이트의 문서 사이를 이동할 때까지 모두 단축할 필요는 없습니다.
단축은 암호화나 접근 제어가 아니므로 로그인 인증 링크, 비밀번호 재설정 주소, 비공개 공유 토큰처럼 권한을 담은 URL을 공개 배포하는 수단으로 생각하면 안 됩니다. 공개할 목적지를 정하고, 전달할 매개변수를 구성하고, 최종 이동과 측정 결과를 확인하는 순서가 링크 운영의 기본입니다.
Python으로 주소 처리나 자동화 기능을 더 만들고 싶다면 코딩인파이의 Python 개발 글에서 이어서 읽을 수 있습니다. 예제 코드는 URL 조립을 이해하기 위한 최소 사례이며, URLto의 내부 구현을 복제한 코드는 아닙니다.
공식 문서와 기능 자료 확인: 2026년 9월 28일. 코딩인파이와 URLto는 같은 운영자가 운영합니다.