팀 소개 페이지 - 03

윤수빈·2024년 8월 7일
0
post-custom-banner

기존 기획 내용대로 사이드바 카테고리를 선택하면 우측화면에 html 정보가 변경이되도록 하고싶었다.

html 자체를 변경하는 일이라면 <a href="...html"></a>를 통해 로컬에 있는 해당 파일을 불러오면 된다.

하지만 html 안에서 html을 불러오려고 하니 CORS 에러가 발생했다.

일단 사이드바는 어떤 화면에서도 고정이기 때문에 index.html로 사용했다.

각 카테고리 별로 html 파일을 생성하여 index.html 안에서는 카테고리를 클릭했을 때,

script.js에 있는 html을 불러오는 함수 loadPage(param, callback)를 호출하게 된다.

loadPage 함수에서는 EventListener로 data-page의 값을 param으로 받고 해당 페이지를 로드하게 된다.

index.html ... 

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Website</title>
    <link rel="stylesheet" href="styles.css">
</head>

<body>
    <nav>
        <ul>
            <li><a href="#" data-page="home.html">Home</a></li>
            <li><a href="#" data-page="members.html">Members</a></li>
            <li><a href="#" data-page="join.html">Join</a></li>
            <li><a href="#" data-page="guest_books.html">Guest Book's</a></li>
        </ul>
    </nav>
    
    <script src="scripts.js" type="module"></script>
</body>

</html>
scripts.js ...

// 페이지가 로드되면 실행되게끔 한다.
document.addEventListener("DOMContentLoaded", function () {

	// nav 안에 모든 a 태그에 대해 쿼리를 저장.
    document.querySelectorAll('nav a').forEach(link => {
        link.addEventListener('click', function (event) {
            event.preventDefault();
            const page = this.getAttribute('data-page');
            loadPage(page);
        });
    });

    // 첫 페이지 로드
    loadPage('home.html');
});

function loadPage(page) {
    fetch(page)
        .then(response => {
            if (!response.ok) {
                throw new Error('네트워크 응답 실패');
            }
            return response.text();
        })
        .then(data => {
            document.getElementById('content').innerHTML = data;

            // 페이지에 따라 적절한 콜백 함수 호출
            if (page === 'join.html') {
                joinLoaded();
            } else if (page === 'home.html') {
                homeLoaded();
            }
            // 다른 페이지에 대해서도 처리 ...
        })
        .catch(error => {
            console.error('페이지 로딩 중 에러:', error);
        });
}

이 상태에서 index.html을 실행하고 카테고리를 선택하면..

CORS(Cross Origin Resource Sharing) policy - 교차 출처 리소스 공유 정책 위반이 되어 fetch 에러가 발생한 것을 알 수 있다.

html에서 로컬에 있는 html을 가져올 때 CORS 에러가 발생하는 이유

웹의 경우 누구나 소스코드를 볼 수 있기 때문에 보안에 취약한 점이 있다.
중간에 데이터 혹은 쿠키나 세션을 가로채거나, 클라이언트를 변조할 수도 있다. 이러한 문제를 방지하기 위해 설정한 보안 정책이 바로 CORS 이다.

CORS 보안 정책을 위반하지 않고 리소스를 주고 받기 위해서는 출처 확인을 거쳐야 한다.
출처는 기본적으로 우리가 볼 수 있는 도메인을 본다면 scheme(프로토콜)host(도메인), port 3종류가 기본이며 port의 경우 기본적으로 생략되어 숨겨져있고, 기본으로 설정된 :80 포트 외에 별도로 작성되어 있다면 반드시 해당 포트로 맞춰줘야 한다.

cross-origin이 있다면 same-origin도 있다.
same-origin은 말 그대로 scheme, host, port 3개가 모두 일치하는 것을 의미하고, 셋 중 하나라도 다르면 cross-origin이 된다.

아무튼....

📌 발생 원인

CORS 발생 원인은 바로 로컬 테스트에서 다른 HTML 파일을 로드하려고 했던 것.
Javascript modules - MDN 에 따르면

이렇게 확인이 된다.

바로 첫번째 부터 문제가 되었던 것...

🙄 그럼 SOP는 어떤 문제일까?

  • SOP는 한 Origin에서 로드된 문서 또는 스크립트가 다른 origin의 리소스와 상호작용할 수 있는 방법을 제한하는 중요한 보안 메커니즘
  • 보안을 위협하는 문서를 격리하여 보안 위협으로부터 보호할 수 있다.

즉, 로딩된 위치에 있는 리소스만 접근이 가능하다는 정책이다. host, scheme, port 중 하나라도 다르면 동일 출처 X

하지만 이런 SOP 에서도 예외되는 태그가 있는데 바로 <img>, <link>, <script>, <video>, <audio>, <object>, <embed>, <applet> 은 동일 출처 정책에 해당되지 않는 것.

SOP는 script에서 XMLHttpRequest 혹은 Fetch API를 사용해 다른 출처의 리소스를 요청할 때 적용된다.

📃 해결 방안

어찌되었든 해결 방안을 찾아본 결과 로컬 서버를 띄워 리소스를 요청하면 해결된다고 한다.
혹은 크롬브라우저에 대한 보안 설정을 바꾸면 된다.

크롬자체 설정을 바꾸고 싶지 않았기에 마침 파이썬도 설치가 되어있어서
프롬포트를 index.html 파일이 있는 경로로 설정한 뒤 python -m http.server 8000 으로 로컬 서버를 띄운 다음 localhost:8000 으로 index.html을 열어서 요청하니 해결되었다..

📕 정리

  1. 서버가 없는 상태에서 로컬 HTML안에 다른 HTML을 불러오려고 했기 때문에 CORS 정책에 위반
  2. 결국 html 자체를 변경하는 형태로 바꾸었다.
profile
정의로운 사회운동가
post-custom-banner

2개의 댓글

comment-user-thumbnail
2024년 8월 19일

CORS 문제와 SOP의 개념에 대해 잘 정리해주셔서 감사합니다. 지금과 같이 특정 서버와 포트를 열어 CORS 문제를 해결하는 것은 조금 더 복잡도가 있는 프로젝트에서 유효하게 동작하는 방식입니다. 다만, 이렇게 열어서 CORS 문제를 해결하는 것이 보안과 관련하여 어떤 이슈가 있을 수 있는지, 복잡한 프로젝트에서는 이를 위해 어떠한 방법을 사용하고 있는지를 조금 더 고민해보면 좋을 것 같습니다.

1개의 답글