URI vs URL vs URN: 웹 자원 식별의 핵심 개념 완전 정복

2025. 6. 11. 12:15·Frontend Development
반응형

들어가며

백엔드 개발을 공부하다 보면 URI, URL, URN이라는 용어를 자주 접하게 됩니다. 하지만 이 세 개념의 차이점을 명확히 구분하지 못하는 경우가 많습니다. 특히 면접에서도 자주 나오는 질문 중 하나죠. 이 글에서는 URI, URL, URN의 차이점과 실무에서의 활용법을 체계적으로 알아보겠습니다.


기본 개념: URI, URL, URN이란?

전체 구조 이해하기

URI (Uniform Resource Identifier)
├── URL (Uniform Resource Locator)
└── URN (Uniform Resource Name)

💡 핵심 포인트: URI는 URL과 URN을 포함하는 상위 개념입니다.

정의 비교표

구분 의미 특징 예시
URI 자원을 식별하는 문자열 URL + URN을 포함하는 상위 개념 모든 웹 주소와 식별자
URL 자원의 위치를 나타내는 주소 어디에 있는지(Where) https://example.com/page
URN 자원의 이름을 나타내는 식별자 무엇인지(What) urn:isbn:123456789

실생활 비유: 집 주소 체계로 이해하기

우편 시스템에 비유

URI (전체 식별 체계)는 사람이나 장소를 찾는 모든 방법과 같습니다.

URL (주소 기반)은 "서울시 강남구 테헤란로 123, 456호"와 같습니다:

  • 정확한 위치 정보 제공
  • 그 위치에 가면 찾고자 하는 것을 발견할 수 있음
  • 위치가 바뀌면 주소도 바뀜

URN (이름 기반)은 "김철수 (주민등록번호: 123456-1234567)"와 같습니다:

  • 이사를 가도 동일한 식별자 유지
  • 위치와 무관한 고유한 이름
  • 영구적인 식별 가능

URI 상세 분석

URI의 구성 요소

https://www.example.com:8080/path/to/resource?query=value#fragment

구성 요소 분해:

요소 값 설명
scheme https 프로토콜 (HTTP, HTTPS, FTP 등)
authority www.example.com:8080 호스트명과 포트번호
path /path/to/resource 자원의 경로
query query=value 쿼리 파라미터
fragment fragment 문서 내 특정 부분

다양한 URI 스키마 예시

// 웹 리소스
const webURI = "https://api.example.com/users/123";

// 파일 리소스  
const fileURI = "file:///home/user/document.pdf";

// 메일 리소스
const mailURI = "mailto:contact@example.com";

// 전화번호 리소스
const telURI = "tel:+82-10-1234-5678";

// FTP 리소스
const ftpURI = "ftp://files.example.com/download/file.zip";

// 데이터베이스 리소스
const dbURI = "mongodb://localhost:27017/myapp";

URL 상세 분석

URL의 특징과 활용

URL(Uniform Resource Locator)은 자원의 위치를 명시하는 방식입니다.

// RESTful API URL 예시
const apiExamples = {
  // 사용자 목록 조회
  getUsers: "GET https://api.example.com/v1/users",

  // 특정 사용자 조회  
  getUser: "GET https://api.example.com/v1/users/123",

  // 사용자 생성
  createUser: "POST https://api.example.com/v1/users",

  // 사용자 수정
  updateUser: "PUT https://api.example.com/v1/users/123",

  // 사용자 삭제
  deleteUser: "DELETE https://api.example.com/v1/users/123"
};

URL 설계 베스트 프랙티스

// ✅ 좋은 URL 설계
const goodURLs = [
  "https://api.bookstore.com/v1/books",           // 명사 사용
  "https://api.bookstore.com/v1/books/123",       // 계층 구조
  "https://api.bookstore.com/v1/books?category=fiction", // 쿼리 파라미터
  "https://api.bookstore.com/v1/authors/456/books"       // 관계 표현
];

// ❌ 나쁜 URL 설계  
const badURLs = [
  "https://api.bookstore.com/getBooks",           // 동사 사용
  "https://api.bookstore.com/book_list",          // 언더스코어 사용
  "https://api.bookstore.com/BOOKS",              // 대문자 사용
  "https://api.bookstore.com/books/get/123"       // 불필요한 동사
];

상대 URL vs 절대 URL

<!-- 절대 URL: 전체 주소 명시 -->
<a href="https://www.example.com/about">회사 소개</a>
<img src="https://cdn.example.com/images/logo.png" alt="로고">

<!-- 상대 URL: 현재 위치 기준 -->
<a href="/about">회사 소개</a>
<a href="../contact">연락처</a>
<a href="./products/laptop">노트북</a>

<!-- 프로토콜 상대 URL -->
<script src="//cdn.example.com/js/app.js"></script>

URN 상세 분석

URN의 특징과 구조

URN(Uniform Resource Name)은 자원의 이름을 통한 영구적 식별자입니다.

URN 구조:

urn:<namespace-identifier>:<namespace-specific-string>

실제 URN 예시

// 다양한 URN 예시
const urnExamples = {
  // ISBN (도서 식별)
  isbn: "urn:isbn:9780134685991",

  // ISSN (잡지/저널 식별)  
  issn: "urn:issn:1234-5678",

  // UUID (범용 고유 식별자)
  uuid: "urn:uuid:550e8400-e29b-41d4-a716-446655440000",

  // OID (객체 식별자)
  oid: "urn:oid:1.2.3.4.5",

  // IETF RFC 문서
  rfc: "urn:ietf:rfc:3986",

  // 법률 문서
  legal: "urn:lex:us:federal:statute:26:501"
};

URN의 장점

// 도서관 시스템 예시
class Library {
  constructor() {
    this.books = new Map();
  }

  // URN을 키로 사용하여 영구적 식별
  addBook(isbn, title, location) {
    const urn = `urn:isbn:${isbn}`;
    this.books.set(urn, {
      title,
      currentLocation: location, // 위치는 변경 가능
      permanentId: urn           // 식별자는 영구적
    });
  }

  // 책의 위치가 바뀌어도 동일한 URN으로 접근
  moveBook(isbn, newLocation) {
    const urn = `urn:isbn:${isbn}`;
    if (this.books.has(urn)) {
      this.books.get(urn).currentLocation = newLocation;
    }
  }

  findBook(isbn) {
    const urn = `urn:isbn:${isbn}`;
    return this.books.get(urn);
  }
}

// 사용 예시
const library = new Library();
library.addBook("9780134685991", "Clean Architecture", "A-shelf-123");
library.moveBook("9780134685991", "B-shelf-456"); // 위치 변경
console.log(library.findBook("9780134685991"));   // 여전히 찾을 수 있음

실무에서의 활용 사례

1. 웹 개발에서의 활용

// Express.js 라우터 설계
const express = require('express');
const router = express.Router();

// URL 패턴 정의 (REST API)
router.get('/api/v1/users/:id', (req, res) => {
  // URL: https://api.example.com/api/v1/users/123
  const userId = req.params.id;
  res.json({ userId, name: 'John Doe' });
});

// 쿼리 파라미터 활용
router.get('/api/v1/products', (req, res) => {
  // URL: https://api.example.com/api/v1/products?category=electronics&sort=price
  const { category, sort } = req.query;
  res.json({ category, sort });
});

2. 마이크로서비스 아키텍처

# Docker Compose 예시
version: '3.8'
services:
  user-service:
    image: user-service:latest
    environment:
      # 다른 서비스와의 통신을 위한 URL
      - ORDER_SERVICE_URL=http://order-service:3000
      - PAYMENT_SERVICE_URL=http://payment-service:3000

  order-service:
    image: order-service:latest
    environment:
      - USER_SERVICE_URL=http://user-service:3000
      - INVENTORY_SERVICE_URL=http://inventory-service:3000
// 서비스 간 통신
class UserService {
  constructor() {
    this.orderServiceURL = process.env.ORDER_SERVICE_URL;
  }

  async getUserOrders(userId) {
    // URL을 통한 외부 서비스 호출
    const response = await fetch(`${this.orderServiceURL}/orders?userId=${userId}`);
    return response.json();
  }
}

3. 콘텐츠 관리 시스템 (CMS)

// 콘텐츠 식별 시스템
class ContentManager {
  constructor() {
    this.contents = new Map();
  }

  // URN으로 콘텐츠 영구 식별
  createContent(type, id, data) {
    const urn = `urn:content:${type}:${id}`;
    this.contents.set(urn, {
      ...data,
      urn,
      // URL은 환경에 따라 달라질 수 있음
      urls: {
        dev: `https://dev.example.com/content/${type}/${id}`,
        staging: `https://staging.example.com/content/${type}/${id}`,
        prod: `https://example.com/content/${type}/${id}`
      }
    });
    return urn;
  }

  // URN으로 콘텐츠 조회 (환경 무관)
  getContent(urn) {
    return this.contents.get(urn);
  }

  // 환경별 URL 생성
  getContentURL(urn, environment = 'prod') {
    const content = this.contents.get(urn);
    return content?.urls[environment];
  }
}

// 사용 예시
const cms = new ContentManager();
const articleURN = cms.createContent('article', '123', {
  title: 'URI vs URL vs URN',
  author: 'Kim Developer'
});

console.log(cms.getContentURL(articleURN, 'dev'));
// https://dev.example.com/content/article/123

실무 개발 시 주의사항

1. URL 보안 고려사항

// ❌ 보안에 취약한 URL 설계
const vulnerableURLs = [
  "https://api.example.com/users/123/password", // 민감 정보 노출
  "https://api.example.com/admin?token=abc123",  // 토큰을 URL에 노출
  "https://api.example.com/files/../../../etc/passwd" // Path Traversal 취약점
];

// ✅ 보안을 고려한 URL 설계
const secureURLs = [
  "https://api.example.com/users/123/profile",    // 일반 정보만 노출
  "https://api.example.com/admin",                // 토큰은 Header에 포함
  "https://api.example.com/files/safe-directory"  // 안전한 경로만 허용
];

// 보안 헤더 사용 예시
const secureRequest = {
  method: 'GET',
  url: 'https://api.example.com/admin',
  headers: {
    'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
    'Content-Type': 'application/json'
  }
};

2. URL 인코딩과 디코딩

// URL 인코딩이 필요한 경우
const searchQuery = "JavaScript URI 인코딩";
const encodedQuery = encodeURIComponent(searchQuery);
const searchURL = `https://search.example.com/q=${encodedQuery}`;
console.log(searchURL);
// https://search.example.com/q=JavaScript%20URI%20%EC%9D%B8%EC%BD%94%EB%94%A9

// URL 디코딩
const decodedQuery = decodeURIComponent(encodedQuery);
console.log(decodedQuery); // "JavaScript URI 인코딩"

// 전체 URI vs 컴포넌트별 인코딩
const fullURI = "https://example.com/path with spaces?query=special chars!";
console.log(encodeURI(fullURI));
// https://example.com/path%20with%20spaces?query=special%20chars!

console.log(encodeURIComponent(fullURI));
// https%3A%2F%2Fexample.com%2Fpath%20with%20spaces%3Fquery%3Dspecial%20chars!

3. URI 검증과 파싱

// URI 유효성 검증
function isValidURI(uri) {
  try {
    new URL(uri);
    return true;
  } catch (error) {
    return false;
  }
}

// URI 파싱 유틸리티
class URIParser {
  static parse(uri) {
    try {
      const url = new URL(uri);
      return {
        scheme: url.protocol.slice(0, -1),
        host: url.hostname,
        port: url.port || (url.protocol === 'https:' ? '443' : '80'),
        path: url.pathname,
        query: Object.fromEntries(url.searchParams),
        fragment: url.hash.slice(1)
      };
    } catch (error) {
      throw new Error(`Invalid URI: ${uri}`);
    }
  }

  static build(components) {
    const { scheme, host, port, path, query, fragment } = components;
    let uri = `${scheme}://${host}`;

    if (port && port !== '80' && port !== '443') {
      uri += `:${port}`;
    }

    uri += path || '/';

    if (query && Object.keys(query).length > 0) {
      const queryString = new URLSearchParams(query).toString();
      uri += `?${queryString}`;
    }

    if (fragment) {
      uri += `#${fragment}`;
    }

    return uri;
  }
}

// 사용 예시
const parsed = URIParser.parse('https://api.example.com:8080/users?page=1&size=10#results');
console.log(parsed);
/*
{
  scheme: 'https',
  host: 'api.example.com',
  port: '8080',
  path: '/users',
  query: { page: '1', size: '10' },
  fragment: 'results'
}
*/

const rebuilt = URIParser.build(parsed);
console.log(rebuilt);
// https://api.example.com:8080/users?page=1&size=10#results

면접 대비 핵심 질문과 답변

자주 나오는 면접 질문

Q1: URI와 URL의 차이점을 설명해주세요.

A: URI는 자원을 식별하는 문자열의 상위 개념이고, URL은 자원의 위치를 나타내는 URI의 한 형태입니다. 모든 URL은 URI이지만, 모든 URI가 URL은 아닙니다.

Q2: 언제 URN을 사용하나요?

A: 자원의 위치가 변경되어도 동일한 식별자를 유지해야 할 때 사용합니다. 예를 들어, 도서의 ISBN, 법률 문서의 고유 번호 등이 있습니다.

Q3: REST API 설계 시 URL 네이밍 규칙은?

A:

  • 명사 사용 (동사 X)
  • 소문자 사용
  • 하이픈(-) 사용 (언더스코어 X)
  • 계층 구조 표현
  • 복수형 사용
// ✅ 좋은 예시
const goodAPIs = [
  "GET /api/v1/users",           // 사용자 목록
  "GET /api/v1/users/123",       // 특정 사용자
  "GET /api/v1/users/123/orders" // 사용자의 주문 목록
];

// ❌ 나쁜 예시  
const badAPIs = [
  "GET /api/v1/getUsers",        // 동사 사용
  "GET /api/v1/user_list",       // 언더스코어 사용
  "GET /api/v1/USER/123"         // 대문자 사용
];

정리 및 핵심 인사이트

핵심 요약

  1. URI: 자원을 식별하는 포괄적인 개념 (URL + URN)
  2. URL: 자원의 위치를 나타내는 주소 ("어디에")
  3. URN: 자원의 이름을 나타내는 식별자 ("무엇을")
  4. 관계: URI ⊃ URL, URI ⊃ URN

개발자를 위한 실무 인사이트

언제 무엇을 사용할까?

상황 사용할 개념 이유
웹 API 설계 URL 자원의 위치와 접근 방법이 중요
마이크로서비스 통신 URL 서비스 간 위치 기반 통신
콘텐츠 영구 식별 URN 위치 변경과 무관한 식별 필요
데이터베이스 스키마 URN 레코드의 영구적 식별자

실무 적용 팁:

  • REST API는 직관적인 URL 구조 설계
  • 민감한 정보는 URL이 아닌 헤더나 바디에 포함
  • 캐시와 SEO를 고려한 URL 패턴 사용
  • 버전 관리를 위한 URL 네임스페이스 설계

성능 최적화 고려사항:

  • URL 길이 최적화 (브라우저 제한 고려)
  • 쿼리 파라미터 vs 패스 파라미터 선택
  • 캐시 키로 활용할 URL 구조 설계

URI, URL, URN의 차이를 정확히 이해하면, 더 나은 웹 아키텍처 설계와 API 개발이 가능합니다. 특히 현대의 마이크로서비스와 RESTful API 환경에서 이러한 개념들은 필수적인 기반 지식입니다.


참고 자료

  • RFC 3986 - Uniform Resource Identifier (URI): Generic Syntax
  • RFC 3987 - Internationalized Resource Identifiers (IRIs)
  • W3C - URI Clarification
  • MDN - URL
  • REST API Tutorial - URI vs URL
  • IANA - Uniform Resource Names (URN) Namespaces
반응형
저작자표시 비영리 변경금지 (새창열림)

'Frontend Development' 카테고리의 다른 글

JavaScript 프로토타입 상속: 객체 간 상속의 핵심 메커니즘 완전 정복  (2) 2025.06.20
HTML DOCTYPE 완전 정복: 웹 브라우저의 첫 번째 선택지  (6) 2025.06.12
event.target vs event.currentTarget: JavaScript 이벤트 처리의 핵심 개념 완전 정복  (2) 2025.06.11
CDN(Content Delivery Network) 완전 정복: 웹 성능 최적화의 핵심 기술  (0) 2025.06.11
TypeScript any vs 제네릭 T: 실행 결과로 보는 확실한 차이점  (0) 2025.06.09
'Frontend Development' 카테고리의 다른 글
  • JavaScript 프로토타입 상속: 객체 간 상속의 핵심 메커니즘 완전 정복
  • HTML DOCTYPE 완전 정복: 웹 브라우저의 첫 번째 선택지
  • event.target vs event.currentTarget: JavaScript 이벤트 처리의 핵심 개념 완전 정복
  • CDN(Content Delivery Network) 완전 정복: 웹 성능 최적화의 핵심 기술
Kun Woo Kim
Kun Woo Kim
안녕하세요, 김건우입니다! 웹과 앱 개발에 열정적인 전문가로, React, TypeScript, Next.js, Node.js, Express, Flutter 등을 활용한 프로젝트를 다룹니다. 제 블로그에서는 개발 여정, 기술 분석, 실용적 코딩 팁을 공유합니다. 창의적인 솔루션을 실제로 적용하는 과정의 통찰도 나눌 예정이니, 궁금한 점이나 상담은 언제든 환영합니다.
  • Kun Woo Kim
    WhiteMouseDev
    김건우
  • 깃허브
    포트폴리오
    velog
  • 전체
    오늘
    어제
  • 공지사항

    • [인사말] 이제 티스토리에서도 만나요! WhiteMouse⋯
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 분류 전체보기 (100) N
      • Frontend Development (39) N
      • Backend Development (21) N
      • Algorithm (33) N
        • 백준 (11) N
        • 프로그래머스 (17)
        • 알고리즘 (5)
      • Infra (1)
      • 자료구조 (3)
  • 링크

    • Github
    • Portfolio
    • Velog
  • 인기 글

  • 태그

    frontend development
    tailwindcss
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Kun Woo Kim
URI vs URL vs URN: 웹 자원 식별의 핵심 개념 완전 정복
상단으로

티스토리툴바