[TIL-0514] <a> 태그와 <Link> 태그

jiny·2025년 5월 27일

캡스톤2

목록 보기
13/22

지역 설명 모달에서 오른쪽 하단에 위치한 '숙소'와 '항공권' 버튼에 외부 사이트를 연결할 때, <Link> 태그를 사용하려고 했다.
그런데 외부 URL을 연결할 때는 <a> 태그를 쓰는 게 안전하다고 해서, <a> 태그와 <Link> 태그의 차이점에 대해서 알아봐야겠다고 생각했다.

항목<a> 태그<Link> 태그
(react-router-dom 기준)
역할하이퍼링크 (전통적인 웹 방식)클라이언트 사이드 라우팅 (SPA 방식)
전체 페이지 새로고침O (기본 동작)X (히스토리만 바꿈)
사용 위치HTML 어디서나React 앱 내부에서만 (.tsx, .jsx, .js 등의 React 컴포넌트 파일 안에서만)
사용 방법<a href="/about">About</a><Link to="/about">About</Link>
의도외부 또는 서버 내 URL 이동리액트 라우터 내 컴포넌트 전환
기능브라우저가 직접 이동 처리라우터가 JS로 이동 처리
예시페이지 전체를 새로 로딩해야 하는 외부 링크나, 비-SPA 사이트에 유용React SPA 내부에서 빠르게 화면 전환이 필요할 때 유용 (이전 상태도 유지됨)
📁 public/
   └── index.html       ← 이건 React 외부. 여기선 <a>만 사용 가능
📁 src/
   └── App.tsx          ← 이건 React 내부. 여기서만 <Link> 사용 가능
   └── components/
       └── Header.tsx   ← 이것도 React 내부

  • <Link>는 SPA 내부 라우팅 전용

    • React Router의 <Link> : Single Page Application(SPA)에서 페이지 새로고침 없이 내부 경로로 이동하기 위한 컴포넌트
    • 내부 경로 예시: /about, /product/123, /mypage
    • <Link>는 내부 이동 시 브라우저 전체를 다시 렌더링하지 않고, 라우터 상태만 바꿔줌
  • 외부 URL을 <Link to="https://..." />처럼 쓰는 건 문제가 될 수 있는 이유

    1. 예외적인 동작 가능성
      <Link>는 내부 경로를 history.push()로 처리하는데, 외부 URL에서는 이 방식이 맞지 않아 예상치 못한 동작이 발생할 수 있음

    2. 의도와 다름
      외부 URL은 페이지 전체를 새로 로드해야 하는데 <Link>는 그걸 막으려 하기 때문에, 브라우저 전체를 새로 여는 동작이 자연스럽지 않음

    3. 리디렉션 오류 가능성
      react-router외부 URL을 내부 URL로 잘못 해석해서 라우팅 에러가 날 수 있음

    4. SEO & 접근성 이슈
      <a> 태그는 웹 크롤러와 스크린리더가 외부 링크로 인식하지만, <Link>는 그렇지 않을 수 있음

  • 그래서 외부 URL은 반드시 <a>로 써야 할까?
    맞다🙆. 외부 링크는 HTML의 <a> 태그를 사용하는 것이 표준이고 안전하다.

    <a href="https://www.booking.com" target="_blank" rel="noopener noreferrer">
     숙소
    </a>

🌟 rel="noopener noreferrer"의 의미

  1. noopener
    • 새 탭으로 열린 페이지(target="_blank"일 때)가 열린 쪽 페이지(window.opener)에 접근하지 못하게 막음
    • 즉, 보안 취약점(window.opener 조작)을 방지
    • 왜 중요할까?
      // A.html에서 B.html을 새 창으로 열었을 때
      const newTab = window.open('https://example.com');
      ➡️ 이 경우 example.comwindow.opener를 통해 A.html을 제어할 수 있음
      // B.html
      window.opener.location = 'https://phishing.com'; // 피싱 가능
      즉, 사용자가 클릭한 링크가 악성 사이트였다면, 기존 창(A.html)을 피싱 사이트로 바꿀 수 있는 심각한 보안 위험이 생김
      ➡️ rel="noopener"을 설정하면 이걸 차단함
  1. noreferrer
    • 브라우저가 referer(레퍼러) 헤더를 전송하지 않도록 함
    • 즉, 내 웹사이트 주소가 상대방 사이트에 전달되지 않음
      Referer: https://my-site.com
      • 기본적으로 이 헤더가 같이 전송되어 상대 사이트가 "어디서 왔는지"를 알 수 있음
      • 민감한 페이지에서 외부로 이동할 경우 개인정보 유출 위험도 있을 수 있음
        ➡️ noreferrer는 이런 정보 노출을 차단함
  • noopener vs. noreferrer 정리

    속성보호 대상주요 기능필요 상황
    noopener내 사이트window.opener 차단 (이전 창 조작 방지)보안 위협 방지
    noreferrer사용자 프라이버시Referer 헤더 제거개인정보 보호
  • 언제 써야 할까?
    항상 target="_blank"를 쓸 때 함께 사용함
    <a href="https://example.com" target="_blank" rel="noopener noreferrer">링크 열기</a>

🌟 rel="noopener" 속성이 없을 때 생기는 보안 문제

  • 문제 상황 데모

    • index.html : 원래 페이지 (사용자 사이트)
      <!-- index.html -->
      <!DOCTYPE html>
      <html lang="ko">
      <head>
        <meta charset="UTF-8" />
        <title>My Website</title>
      </head>
        <body>
          <h1>안전한 사이트</h1>
          <a href="attacker.html" target="_blank">새 창으로 이동</a>
        </body>
      </html>
    • attacker.html : 외부 악성 페이지 (공격자 사이트)
      <!-- attacker.html -->
      <!DOCTYPE html>
      <html lang="ko">
      <head>
        <meta charset="UTF-8" />
        <title>악성 페이지</title>
        <script>
          // 창이 열리자마자 이전 창을 공격
          if (window.opener) {
          	window.opener.location.href = "https://phishing-site.com";
          }
        </script>
      </head>
      <body>
        <h1>이 페이지는 이전 창을 조합니다!</h1>
      </body>
      </html>
  • 동작 흐름

    1. 사용자가 index.html에서 링크를 클릭해서 attacker.html을 새 탭으로 엶
    2. attacker.htmlwindow.opener를 통해 index.html을 조작함
    3. window.opener.location.href = "https://phishing-site.com"에 의해 원래 창이 피싱 사이트로 바뀜
  • 해결 방법: rel="noopener" 추가
    <a href="attacker.html" target="_blank" rel="noopener">새 창으로 이동</a>
    이렇게 하면 attacker.htmlwindow.openernull이 되기 때문에 이전 창을 조작할 수 없음

0개의 댓글