JavaScript/JavaScript 입문

<script> 태그로 JS 불러오기 - Inline, Internal, External 방식 완전 가이드

코딩하는 패션이과생 2025. 5. 15. 23:39
반응형

script 태그란?

<script> 태그는 HTML 문서에 JavaScript 코드를 포함시키거나 외부 JavaScript 파일을 연결하는 HTML 요소입니다. 웹페이지에 동적 기능을 추가하기 위해 반드시 필요한 태그입니다.

🎯 script 태그의 역할

  • JavaScript 코드 실행
  • 외부 JavaScript 파일 연결
  • 브라우저에게 JavaScript 코드 위치 알림
  • 코드 실행 타이밍 제어

Inline JavaScript (인라인 방식)

📝 인라인 방식이란?

인라인 JavaScript는 HTML 요소의 속성 안에 직접 JavaScript 코드를 작성하는 방식입니다.

기본 문법

<!-- onclick 속성에 JavaScript 코드 작성 -->
<button onclick="alert('버튼 클릭!')">클릭하세요</button>

<!-- onmouseover 이벤트 -->
<div onmouseover="this.style.backgroundColor='yellow'">
    마우스를 올려보세요
</div>

<!-- href 속성에 JavaScript 코드 -->
<a href="javascript:alert('링크 클릭!')">JavaScript 링크</a>

💭 실제 예제

<!DOCTYPE html>
<html>
<head>
    <title>인라인 JavaScript 예제</title>
</head>
<body>
    <h1>인라인 JavaScript 데모</h1>

    <!-- 버튼 클릭 이벤트 -->
    <button onclick="changeTitle()">제목 변경</button>

    <!-- 색상 변경 버튼 -->
    <button onclick="document.body.style.backgroundColor='lightblue'">
        배경색 변경
    </button>

    <!-- 현재 시간 표시 -->
    <button onclick="alert('현재 시간: ' + new Date().toLocaleTimeString())">
        시간 보기
    </button>

    <script>
        function changeTitle() {
            document.getElementsByTagName('h1')[0].textContent = '제목이 변경되었습니다!';
        }
    </script>
</body>
</html>

✅ 인라인 방식의 장점

  • 간단함: 작은 기능을 빠르게 추가 가능
  • 직관적: 요소와 동작이 한눈에 보임
  • 테스트: 프로토타이핑이나 간단한 테스트에 유용

❌ 인라인 방식의 단점

  • 유지보수 어려움: HTML과 JavaScript가 섞여 있음
  • 재사용성 부족: 같은 코드 반복 작성
  • 보안 문제: XSS 공격에 취약할 수 있음
  • 가독성 저하: 복잡한 코드를 작성하기 어려움

Internal JavaScript (내부 방식)

📄 내부 방식이란?

내부 JavaScript는 HTML 문서의 <script> 태그 안에 JavaScript 코드를 직접 작성하는 방식입니다.

기본 문법

<script>
    // JavaScript 코드 작성
    console.log("Hello World");
</script>

💡 실제 예제

<!DOCTYPE html>
<html>
<head>
    <title>내부 JavaScript 예제</title>
    <script>
        // 페이지 로드 전에 실행
        console.log("head의 script 실행");

        // 함수 정의
        function showWelcome() {
            alert("환영합니다!");
        }
    </script>
</head>
<body>
    <h1 id="title">내부 JavaScript 데모</h1>
    <button onclick="showWelcome()">환영 메시지</button>
    <button onclick="changeColor()">색상 변경</button>
    <button onclick="showInfo()">정보 보기</button>

    <script>
        // body 끝에서 실행 - DOM 요소에 접근 가능
        console.log("body의 script 실행");

        function changeColor() {
            const title = document.getElementById('title');
            const colors = ['red', 'blue', 'green', 'purple', 'orange'];
            const randomColor = colors[Math.floor(Math.random() * colors.length)];
            title.style.color = randomColor;
        }

        function showInfo() {
            const info = {
                title: document.getElementById('title').textContent,
                time: new Date().toLocaleString(),
                url: window.location.href
            };

            alert(`제목: ${info.title}\n시간: ${info.time}\nURL: ${info.url}`);
        }

        // 페이지 로드 완료 후 실행
        window.onload = function() {
            console.log("페이지 로드 완료!");
        };
    </script>
</body>
</html>

📍 script 태그 위치별 특징

<!DOCTYPE html>
<html>
<head>
    <script>
        // ❌ DOM 요소에 접근 불가 - 아직 로드되지 않음
        // const title = document.getElementById('title'); // null

        // ✅ 함수 정의는 가능
        function initPage() {
            console.log("페이지 초기화");
        }
    </script>
</head>
<body>
    <h1 id="title">제목</h1>

    <script>
        // ✅ 위의 DOM 요소에 접근 가능
        const title = document.getElementById('title');
        title.style.color = 'blue';

        // ✅ head의 함수 호출 가능
        initPage();
    </script>
</body>
</html>

✅ 내부 방식의 장점

  • 관리 용이: 한 파일에서 HTML과 JavaScript 관리
  • 빠른 로딩: 별도 파일 요청 없어 로딩 시간 단축
  • 프로토타이핑: 개발 초기 단계에서 유용

❌ 내부 방식의 단점

  • 파일 크기: HTML 파일이 커질 수 있음
  • 재사용 불가: 다른 페이지에서 사용 불가
  • 캐싱 불가: JavaScript 코드 재사용으로 인한 성능 저하

External JavaScript (외부 방식)

🔗 외부 방식이란?

외부 JavaScript는 별도의 .js 파일에 JavaScript 코드를 작성하고, HTML에서 src 속성으로 연결하는 방식입니다.

기본 문법

<script src="파일경로/script.js"></script>

📁 파일 구조 예제

project/
├── index.html
├── about.html
└── js/
    ├── main.js
    ├── utils.js
    └── app.js

💾 실제 예제

main.js 파일:

// main.js
console.log("외부 JavaScript 파일 로드됨");

// 전역 변수
let pageLoadTime = new Date();

// 유틸리티 함수들
function formatDate(date) {
    return date.toLocaleDateString('ko-KR');
}

function formatTime(date) {
    return date.toLocaleTimeString('ko-KR');
}

// 페이지 초기화 함수
function initializePage() {
    // 페이지 로드 시간 표시
    const loadTimeElement = document.getElementById('loadTime');
    if (loadTimeElement) {
        loadTimeElement.textContent = `페이지 로드 시간: ${formatTime(pageLoadTime)}`;
    }

    // 이벤트 리스너 등록
    setupEventListeners();
}

function setupEventListeners() {
    // 모든 버튼에 클릭 이벤트 추가
    const buttons = document.querySelectorAll('button');
    buttons.forEach(button => {
        button.addEventListener('click', function() {
            console.log(`버튼 클릭됨: ${this.textContent}`);
        });
    });
}

// DOM 로드 완료 후 실행
document.addEventListener('DOMContentLoaded', initializePage);

utils.js 파일:

// utils.js - 유틸리티 함수들
const Utils = {
    // 랜덤 색상 생성
    getRandomColor() {
        const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7'];
        return colors[Math.floor(Math.random() * colors.length)];
    },

    // 요소 애니메이션
    animateElement(element, duration = 300) {
        element.style.transition = `all ${duration}ms ease`;
        element.style.transform = 'scale(1.1)';

        setTimeout(() => {
            element.style.transform = 'scale(1)';
        }, duration);
    },

    // 로컬 스토리지 헬퍼
    storage: {
        set(key, value) {
            localStorage.setItem(key, JSON.stringify(value));
        },

        get(key) {
            const item = localStorage.getItem(key);
            return item ? JSON.parse(item) : null;
        }
    }
};

index.html 파일:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>외부 JavaScript 예제</title>

    <!-- CDN에서 외부 라이브러리 로드 -->
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
    <h1>외부 JavaScript 데모</h1>
    <p id="loadTime"></p>

    <button onclick="changeBackgroundColor()">배경색 변경</button>
    <button onclick="showCurrentTime()">현재 시간</button>
    <button onclick="saveToStorage()">데이터 저장</button>
    <button onclick="loadFromStorage()">데이터 불러오기</button>

    <!-- 외부 JavaScript 파일들 로드 -->
    <script src="js/utils.js"></script>
    <script src="js/main.js"></script>

    <!-- 페이지별 스크립트 -->
    <script>
        function changeBackgroundColor() {
            document.body.style.backgroundColor = Utils.getRandomColor();
            Utils.animateElement(document.body);
        }

        function showCurrentTime() {
            alert(`현재 시간: ${formatTime(new Date())}`);
        }

        function saveToStorage() {
            const data = {
                timestamp: new Date().toISOString(),
                page: 'index',
                visits: (Utils.storage.get('visits') || 0) + 1
            };

            Utils.storage.set('pageData', data);
            alert('데이터가 저장되었습니다!');
        }

        function loadFromStorage() {
            const data = Utils.storage.get('pageData');
            if (data) {
                alert(`저장된 데이터:\n마지막 방문: ${formatDate(new Date(data.timestamp))}\n방문 횟수: ${data.visits}`);
            } else {
                alert('저장된 데이터가 없습니다.');
            }
        }
    </script>
</body>
</html>

✅ 외부 방식의 장점

  • 재사용성: 여러 HTML 파일에서 동일한 JS 파일 사용
  • 유지보수: 코드 분리로 관리가 용이
  • 캐싱: 브라우저가 JS 파일을 캐시하여 성능 향상
  • 협업: 팀 개발 시 역할 분담 가능
  • 모듈화: 기능별로 파일 분리 가능

❌ 외부 방식의 단점

  • HTTP 요청: 추가 네트워크 요청 필요
  • 경로 관리: 파일 경로 오류 가능성
  • 의존성: 파일 로드 순서 고려 필요

세 방식 비교 분석

📊 종합 비교표

특징 Inline Internal External
코드 위치 HTML 속성 내 <script> 태그 내 별도 .js 파일
재사용성 ❌ 낮음 ❌ 불가능 ✅ 높음
유지보수 ❌ 어려움 ⚠️ 보통 ✅ 쉬움
성능 ✅ 빠름 ✅ 빠름 ⚠️ 추가 요청
캐싱 ❌ 불가능 ❌ 불가능 ✅ 가능
디버깅 ❌ 어려움 ⚠️ 보통 ✅ 쉬움
협업 ❌ 어려움 ❌ 어려움 ✅ 쉬움

🎯 언제 어떤 방식을 사용할까?

Inline JavaScript 사용 시기:

  • 매우 간단한 이벤트 처리 (1-2줄)
  • 프로토타이핑이나 테스트
  • 이메일 템플릿 (외부 파일 불가)

Internal JavaScript 사용 시기:

  • 해당 페이지에만 필요한 코드
  • 작은 프로젝트나 단일 페이지
  • 서버 사이드 렌더링 최적화

External JavaScript 사용 시기:

  • 재사용 가능한 기능
  • 대규모 프로젝트
  • 팀 개발 환경
  • 성능 최적화가 중요한 경우

script 태그의 추가 속성

🔄 async와 defer 속성

<!-- 기본 방식: HTML 파싱 중단하고 JS 실행 -->
<script src="script.js"></script>

<!-- async: 백그라운드에서 로드하고 완료되면 즉시 실행 -->
<script src="script.js" async></script>

<!-- defer: 백그라운드에서 로드하고 HTML 파싱 완료 후 실행 -->
<script src="script.js" defer></script>

📈 로딩 순서 비교

일반 script:     HTML 파싱 ──┐     ┌─── HTML 파싱
                             ↓     ↑
                        JS 다운로드 + 실행

async script:    HTML 파싱 ────────────────────
                 JS 다운로드 ──┐
                              ↓
                             JS 실행

defer script:    HTML 파싱 ─────────────────┬─
                 JS 다운로드 ───────────────┘
                                           ↓
                                         JS 실행

🔧 기타 유용한 속성

<!-- 타입 지정 (ES6 모듈) -->
<script type="module" src="module.js"></script>

<!-- 무결성 검증 -->
<script src="https://cdn.example.com/library.js" 
        integrity="sha384-abc123..."
        crossorigin="anonymous"></script>

<!-- noscript 대체 내용 -->
<noscript>
    <p>JavaScript가 비활성화되어 있습니다.</p>
</noscript>

실무에서의 모범 사례

🏆 권장 사항

  1. 외부 파일 우선 사용
    <!-- ✅ 권장 -->
    <script src="js/main.js" defer></script>
    


2. **로딩 순서 최적화**
```html
<!DOCTYPE html>
<html>
<head>
    <!-- 필수 라이브러리는 head에 -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
</head>
<body>
    <!-- 콘텐츠 -->

    <!-- 페이지별 스크립트는 body 끝에 -->
    <script src="js/utils.js"></script>
    <script src="js/main.js"></script>
    <script src="js/page-specific.js"></script>
</body>
</html>
  1. 환경별 파일 로드
    <!-- 개발 환경 -->
    <script src="js/app.js"></script>
    


### 🛡️ 보안 고려사항

```html
<!-- CSP (Content Security Policy) 설정 -->
<meta http-equiv="Content-Security-Policy" 
      content="script-src 'self' https://trusted-cdn.com;">

<!-- 신뢰할 수 있는 CDN 사용 -->
<script src="https://cdn.jsdelivr.net/npm/library@1.0.0/dist/library.min.js"
        integrity="sha384-..."
        crossorigin="anonymous"></script>

완전한 예제

🎉 실제 프로젝트 구조

프로젝트 파일 구조:

my-website/
├── index.html
├── about.html
├── css/
│   └── style.css
└── js/
    ├── config.js      # 설정 파일
    ├── utils.js       # 유틸리티 함수
    ├── components.js  # UI 컴포넌트
    └── main.js        # 메인 로직

index.html:

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript 로딩 방식 완전 예제</title>
    <link rel="stylesheet" href="css/style.css">

    <!-- 설정 파일 먼저 로드 -->
    <script src="js/config.js"></script>
</head>
<body>
    <header>
        <h1>JavaScript 로딩 방식 데모</h1>
        <nav>
            <button onclick="showSection('inline')">Inline</button>
            <button onclick="showSection('internal')">Internal</button>
            <button onclick="showSection('external')">External</button>
        </nav>
    </header>

    <main>
        <section id="inline" class="demo-section">
            <h2>Inline JavaScript</h2>
            <button onclick="alert('Inline 방식 알림!')">
                Inline 알림
            </button>
            <div onmouseover="this.style.backgroundColor='yellow'"
                 onmouseout="this.style.backgroundColor='transparent'">
                마우스를 올려보세요 (Inline)
            </div>
        </section>

        <section id="internal" class="demo-section">
            <h2>Internal JavaScript</h2>
            <button id="internalBtn">Internal 버튼</button>
            <div id="internalDiv">Internal 영역</div>
        </section>

        <section id="external" class="demo-section">
            <h2>External JavaScript</h2>
            <button id="externalBtn">External 버튼</button>
            <div id="counter">카운터: <span id="count">0</span></div>
        </section>
    </main>

    <!-- Internal JavaScript -->
    <script>
        // Internal 방식 데모
        document.getElementById('internalBtn').addEventListener('click', function() {
            const div = document.getElementById('internalDiv');
            div.innerHTML = `<p>Internal 방식으로 업데이트됨! 시간: ${new Date().toLocaleTimeString()}</p>`;
            div.style.backgroundColor = '#e3f2fd';
        });

        // 섹션 표시/숨김 함수
        function showSection(sectionName) {
            const sections = document.querySelectorAll('.demo-section');
            sections.forEach(section => {
                section.style.display = 'none';
            });

            document.getElementById(sectionName).style.display = 'block';
        }

        // 페이지 로드 시 첫 번째 섹션 표시
        window.addEventListener('load', function() {
            showSection('inline');
        });
    </script>

    <!-- External JavaScript 파일들 -->
    <script src="js/utils.js"></script>
    <script src="js/components.js"></script>
    <script src="js/main.js"></script>
</body>
</html>

js/config.js:

// 전역 설정
window.APP_CONFIG = {
    version: '1.0.0',
    debug: true,
    apiUrl: 'https://api.example.com',
    colors: {
        primary: '#2196F3',
        secondary: '#FF9800',
        success: '#4CAF50',
        error: '#F44336'
    }
};

js/utils.js:

// 유틸리티 함수들
const Utils = {
    log(message) {
        if (APP_CONFIG.debug) {
            console.log(`[${new Date().toISOString()}] ${message}`);
        }
    },

    formatNumber(num) {
        return num.toLocaleString();
    },

    debounce(func, wait) {
        let timeout;
        return function executedFunction(...args) {
            const later = () => {
                clearTimeout(timeout);
                func(...args);
            };
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
        };
    }
};

js/components.js:

// UI 컴포넌트
const Components = {
    createButton(text, onclick) {
        const button = document.createElement('button');
        button.textContent = text;
        button.onclick = onclick;
        return button;
    },

    showToast(message, type = 'info') {
        const toast = document.createElement('div');
        toast.className = `toast toast-${type}`;
        toast.textContent = message;

        document.body.appendChild(toast);

        setTimeout(() => {
            toast.remove();
        }, 3000);
    }
};

js/main.js:

// 메인 애플리케이션 로직
let counter = 0;

// External 방식 데모
function initExternalDemo() {
    const btn = document.getElementById('externalBtn');
    const countSpan = document.getElementById('count');

    btn.addEventListener('click', Utils.debounce(function() {
        counter++;
        countSpan.textContent = Utils.formatNumber(counter);

        Utils.log(`카운터 업데이트: ${counter}`);
        Components.showToast(`카운터가 ${counter}로 업데이트되었습니다!`, 'success');

        // 색상 변경
        btn.style.backgroundColor = counter % 2 === 0 ? 
            APP_CONFIG.colors.primary : APP_CONFIG.colors.secondary;
    }, 300));
}

// DOM 로드 완료 후 실행
document.addEventListener('DOMContentLoaded', function() {
    Utils.log('애플리케이션 초기화됨');
    initExternalDemo();
});

마무리

<script> 태그를 사용한 JavaScript 로딩 방식은 웹 개발의 핵심 기초입니다. 각 방식의 특징을 이해하고 상황에 맞게 선택하는 것이 중요합니다.

✅ 핵심 정리

  1. Inline: 간단한 이벤트 처리용
  2. Internal: 페이지별 특화 코드용
  3. External: 재사용 가능한 모듈용

🚀 실무 권장사항

  1. 가능한 한 External 방식 우선 사용
  2. defer 속성으로 로딩 최적화
  3. 파일을 기능별로 분리하여 관리
  4. 캐싱과 압축을 통한 성능 향상

이제 여러분은 JavaScript를 HTML에 효과적으로 연결하는 방법을 알게 되었습니다. 실제 프로젝트에서 이 지식을 활용해보세요!

이 글이 JavaScript 연결 방식 이해에 도움이 되셨나요? 어려운 부분이나 추가 질문이 있다면 댓글로 남겨주세요! 💬