JavaScript 주요 문법 (3)

조혜준·2023년 10월 24일

[1] 모듈

일반적으로 웹사이트는 여러개의 자바스크립트로 이루어져 있음
자바스크립트는 파일들을 각각 별개의 프로그램으로 취급

  • 문제 발생! 웹사이트가 하는 일이 많아짐에 따라 스크립트 파일의 대거 증가

예전 자바스크립트 파일들은 전역 스코프에 존재하는 변수/함수를 사용해야 했음

즉시 실행함수 등을 통해 전역 스코프가 오염되는 것을 막을수는 있었지만, 스크립트 파일간 의존도를 확인하기 힘들고 실행순서를 제어해야한다는 한계점 존재!

  • 모듈 등장.

모듈 : 설계 시점에 의미있는 요소 (우리가 의식적으로 나눠놓은 요소)

컴포넌트 : 런타임 시점에 의미있는 요소 (나눠놓은 요소에 포함되어 실행되는 요소)


모듈

  • import와 export를 통해 모듈 불러오기와 내보내기를 수행할 수 있음

모듈의 특징

  1. 항상 use strict으로 실행됨
  2. 모듈 레벨 스코프가 있음
  3. 단 한번만 평가됨
  4. 지연 실행됨

Webpack 등을 이용하여 번들링한 스크립트를 불러오면 크게 type="module"을 사용할 일이 별로 없음



[2] 유니코드

기본 개념 - CSS (Coded Character Set)

  • 문자들은 Code Point에 대응시켜 만든 코드화된 문자들의 집합
  • Code Point는 Character의 식별자가 됨
  • KS C 5601, ISO 10646, US-ASC||, ...

기본 개념 - CES (Character Encoding Scheme)

  • CCS를 octet(8bit) 집합에 대응시키는 것
  • CCS와 CES는 1:1로 대응
  • 흔히 말하는 인코딩에 해당
    • 인코딩 : Character을 시스템이 인식할 수 있는 값으로 변환하는 것
    • 디코딩 : 인코딩된 값을 다시 Character로 변환하는 것
  • UTF-8, UTF-16, euc-kr, CP949, ...

기본 개념 - TES(Transfer Encoding Syntax)

  • 인코딩한 문자가 특정 프로토콜을 타고 전송되도록 변환하는 것
  • 통신 프로토콜에 제약이 있을 수 있기 때문
    • 예를 들어, URL에서 공백은 사용할 수 없기에 변환을 해야 함
  • URL Encoding, BASE64 Encoding, ...

유니코드

  • 유니코드가 없을 때에도 각 나라들은 알아서 자국 문자를 잘 표현함
  • 하지만, 다양한 나라가 서로 다른 인코딩 방식을 사용함으로써 호환성 및 확장성에 문제를 일으킴
    • 심지어 같은 문자여도 깨지는 경우가 발생
  • 결국 전 세계 문자를 컴퓨터에서 다룰 수 있도록 표준 시스템을 만들음
  • https://home.unicode.org/

유니코드 - CSS

  • 코드 포인트 범위 - 0x0 ~ 0x0FFFF (1114112개 문자)
  • Surrogate Pair 방법을 이용해 2바이트보다 큰 문자를 표현

유니코드 - CES

  • Code Point가 어떤 단위로 조합되어 인코딩되는 지 정의한 것
  • Big-Endian과 Little-Endian은 컴퓨터의 메모리에 저장된 바이트의 순서를 말함
  • Big-Endian은 큰 쪽에서 작은 쪽으로 저장됨
  • Little-Endian은 작은 쪽에서 큰 쪽으로 저장됨
  • BOM(Byte Order Mark)
  • 문서 제일 앞에 U+FEFF를 삽입해 어플리케이션이 바이트 순서를 알 수 있게 해줌



[3] 정규표현식

정규표현식

  • 목적 : 패턴을 이용하여 문자를 검색(search), 대체(replace), 추출(extract)할 수 있음
  • 성능은 매우 느리지만, 매우 편함
  • 정규표현식을 사용하기 위해서는 패턴을 찾는 것이 매우 중요

    https://rubular.com/

정규표현식 표현

Ex) 휴대폰 번호를 찾아보자.

Q : 휴대폰 번호의 패턴은?

A : 세자리 숫자, 하이픈, 셋 혹은 네 자리 숫자, 하이픈, 네 자리 숫자 패턴으로 이루어짐
-> \d{3}-\d{3,4}-\d{4}


Ex) 이메일의 주소를 찾아보자.

Q : 이메일의 패턴은?

A : 문자열, @, 문자열, ., 문자열 패턴으로 이루어짐
-> .+@.+..+


Q : 여기서 naver, grepp과 같은 중간 문자열만 뽑고 싶다면?

A : 캡처 (...)를 이용하면 원하는 부분만 뽑아낼 수 있음 (여러번 사용 가능)
-> .+@(.+)..+


JavaScript에서의 정규표현식

생성 방법

  • JavaScript는 RegExp 객체로 정규표현식 기능을 제공
  • Array, Object 처럼 Literal로 생성 가능
// 생성자 함수 방식
// new RegExp(표현식)
const regExp1 = new RegExp("^\d+");

// new RegExp(표현식, 플래그)
const regExp2 = new RegExp("^\d+", "gi");

// 리터럴 방식
// /표현식/
const regexp1 = /^\d+/;

// /표현식/플래그
> const regexp2 = /^\d+/gi;

test

  • 정규표현식 객체의 test함수는 입력받은 문자열에 찾는 패턴이 있는지 찾은 후, 있다면 true를 없으면 false를 반환
const message = "안녕하세요. 010-1234-5678로 연락주세요.";
const message2 = "안녕하세요. 연락하지 마세요.";

// 정규표현식 리터럴
const regExp = /\d{3}-\d{3,4}-\d{4}/;
console.log(regExp.test(message));       // true
console.log(regExp.test(message2));      // flase

exec

  • 정규표현식 객체의 exec 함수는 입력받은 문자열에 찾는 패턴이 있는지 찾은 후 일치하는 패턴 정보를 반환하고, 없으면 null을 반환
  • 문자 추출
const message = "안녕하세요. 010-1234-5678로 연락주세요.";
const message2 = "안녕하세요. 연락하지 마세요.";

// 정규표현식 리터럴
const regExp = /\d{3}-\d{3,4}-\d{4}/;
console.log(regExp.exec(message));   
console.log(regExp.exec(message2));


match

  • String 객체의 replace 함수는 정규표현식 객체를 파라미터로 받아 패턴이 있는지 찾은 후 일치하는 패턴 정보를 반환한고 없으면 null을 반환
  • 정규표현식 객체의 exec와 같음
  • 문자 추출
const message = "안녕하세요. 010-1234-5678로 연락주세요.";
const message2 = "안녕하세요. 010-1234-5678말고, 010-9876-5432로 연락주세요.";

// 정규표현식 리터럴
const regExp = /\d{3}-\d{3,4}-\d{4}/;
// 무조건 처음 매칭된 것을 반환
console.log(message.match(regExp));   
console.log(message2.match(regExp));   
//모두 탐색하려면 matchAll을 사용해야 함
console.log([...message2.matchAll(/\d{3}-\d{3,4}-\d{4}/g)]);


replace

  • String 객체의 replace 함수는 정규표현식 객체를 파라미터로 받아 패턴이 있는지 찾은 후 일치하는 패턴 정보를 원하는 문자열로 바꿀 수 있음
  • 문자 대체
const message = "안녕하세요. 010-1234-5678로 연락주세요.";
const message2 = `안녕하세요. 010-1234-5678말고, 010-9876-5432로 연락주세요.`

// 정규표현식 리터럴
const regExp = /\d{3}-\d{3,4}-\d{4}/;
console.log(message.replace(regExp, "전화번호")); 
console.log(message2.replace(regExp, "전화번호"));
// 옵션으로 g를 붙이면 모두 변경
console.log(message2.replace(/\d{3}-\d{3,4}-\d{4}/g, "전화번호"));


search

  • String 객체의 replace 함수는 정규표현식 객체를 파라미터로 받아 패턴이 있는지 찾은 후 일치하는 패턴 정보의 위치를 반환
  • 문자 검색
const message = "안녕하세요. 010-1234-5678로 연락주세요.";
const message2 = `안녕하세요. 010-1234-5678말고, 010-9876-5432로 연락주세요.`

// 정규표현식 리터럴
const regExp = /\d{3}-\d{3,4}-\d{4}/;
// 무조건 처음 매칭된 것을 반환
console.log(message.search(regExp));   
console.log(message2.search(regExp));
console.log(message2.search(/\d{3}-\d{3,4}-\d{4}/g));
//모두 탐색하려면 matchAll을 사용해야 함
console.log([...message2.matchAll(/\d{3}-\d{3,4}-\d{4}/g)]);


capture

  • 캡처가 적용된 정규표현식을 이용하면 match 반환값의 1번 인덱스부터 순차적으로 캡처 결과가 들어감
const message = "안녕하세요. 010-1234-5678로 연락주세요.";

// 정규표현식 리터럴
const regExp = /(\d{3})-(\d{3,4})-(\d{4})/;
console.log(message.match(regExp));


Run-length encoding

  • 매우 간단한 비손실 압축 방법
  • "AAAAAABBBDFFFFFFFKK" 문자열을 어떻게 압축할까?
    - "6A3B1D7F2K"로 압축할 수 있음 (6개의 A, 3개의 B, 1개의 D, 7개의 F, 2개의 K)
    • -> (.)\1*
      -> A, B, C로 각각 그룹화됨을 알 수 있음
const raw = "AAAAAABBBDFFFFFFFKK";
const compressed = "6A3B1D7F2K";

const regExp = /(.)\1*/g;
const result = raw
  .match(regExp)
  .reduce((a, b) => 
    a + `${b.length}${b.slice(0, 1)}`, "");

console.log(result);
console.log(result === compressed);



[4] 쿠키와 세션, 웹 스토리지

HTTP 통신

  • HTTP Request는 기본적으로 상태가 존재하지 않음
  • 따라서 서버는 어떤 브라우저에서 요청이 온 것인지 알 수 없음
  • 이때, 헤더에 쿠키를 담으면 서버가 쿠키를 읽어 어디서 온 것인지 알 수 있음

Cookie

  • 클라이언트에서 젖아, 관리하는 데이터들
  • 브라우저를 닫아도 데이터를 유지할 수 있음
  • 서버에서 Set-Cookie를 응답 헤더로 내려주면 클라이언트는 받아서 저장
  • 클라이언트에서 자체적으로 조작 가능
  • 각 상태에 수명을 정할 수 있음

Set-Cookie

  • Set-Cookie: 키=값; 옵션
  • 응답 헤더에 담으면 브라우저가 알아서 저장함
  • 각 데이터에는 여러 옵션들이 존재
    • Expires : 쿠키 만료 날짜를 지정
    • Secure : HTTPS에서만 쿠키를 전송
    • HttpOnly : JavaScript에서 쿠키를 접근 못하도록 막음
    • Max-Age : 쿠키 수명을 정함. 이때, Expires는 무시
    • Domain : 도메인이 일치하는 요청만 쿠키가 전송됨
    • Path : 패스와 일치하는 요청만 쿠키가 전송됨

쿠키의 취약점

  • XSS(Cross-Site-Script) 공격을 당할 수 있음
    • JavaScript를 이용해 악의적인 사용자가 다른 사용자의 쿠키값을 탈취할 수 있음
    • 쿠키를 암호화하지 않고 보내면 중간에 탈취 가능성이 있음
      • HTTPS

서버가 사용자를 구분하려면? >> Session

Session

  • HTTP Session Id를 식별자로 사용하고 구분
  • 클라이언트는 HTTP Session Id를 쿠키 형태로 저장
  • 서버 자체적으로 기록하고 관리

세션의 문제점

  • 세션은 서버에 파일로 저장되는데, 만약 사용자가 많아진다면?
  • 서버가 만약 2대라면 세션은 어떻게 관리해야 하는가?

이제는 서버와 클라이언트 간 인증은 별도 토큰을 사용하고, 쿠키는 클라이언트 자체적이고 지속적인 데이터 관리 용도로 많이 사용됨


웹스토리지

  • 클라이언트에 데이터를 저장하기 위한 새로운 방법
  • HTML5부터 등장
  • 쿠키에서 하기 힘든 것들은 지원하기 위해 등장
  • 로컬 스토리지와 세션 스토리지가 있음

로컬 스토리지

  • 로컬 스토리지에 데이터를 저장하면 반영구적으로 데이터가 저장됨
  • 브라우저를 종료해도 계속해서 데이터가 남음
  • 저장했던 도메인과 이용하는 도메인이 다른 경우 접근 불가
  • 쿠카와 마찬가지로 Key-Value의 형태로 저장

세션 스토리지

  • 새 창을 생성할 때마다 개별적으로 저장되는 데이터를 관리
  • 브라우저를 닫는 순간 사라짐
  • 같은 도메인이어도 세션이 다르면 데이터에 접근 불가
  • 쿠카와 마찬가지로 Key-Value의 형태로 저장
// 쿠키 관리는 String으로. 불편
document.cookie = "key=value; key2=value2;"

// 데이터를 저장
localStorage.setItem('name', '이선협');
console.log(localStorage.getItem('name'));	//이선협

// 데이터를 지움
localStorage.removeItem('name');
// 데이터를 전부 지움
localStorage.clear();

// 데이터를 저장
sessionStorage.setItem('name', '이선협');
console.log(sessionStorage.getItem('name'));	//이선협

// 데이터를 지움
sessionStorage.removeItem('name');
// 데이터를 전부 지움
sessionStorage.clear();


[5] HTML과 CSS

HTML (Hyper Text Markup Language)

HTML은 이제 문서 구조(Structure)의미(Semantic)로만 남게 되었다


HTML5

  • 2014년 10월 28일에 표준안이 확정
  • 영상과 음성을 처기하기 위한 멀티미디어 요소가 추가됨
    • audio, video, canvas
  • 구조적인 요소가 추가됨
    • header, nav, articale, section, aside, footer, ...
  • 표현을 담당한던 태그가 삭제되고 의미를 담은 태그가 추가됨
    • s → del
    • b → strong
    • ...

※ 주의할 점!
-> 태그마다 기본 스타일이 있음

// 형식이 정해져있는 리스트 태그
<ul>
  <li>Hello</li>
  <li>HTML</li>
  <li>CSS</li>
  <li>JavaScript</li>
</ul>

CSS를 이용하는 방법
  1. style 태그를 이용하는 방법
  2. inline을 이용하는 방법
  3. 외부 파일을 만들어 링크를 불러오는 방법


[6] DOM

DOM (Document Object Model) : 문서 객체 모델


DOM은 왜 탄생했을까?

  • JavaScript가 탄생하면서 같이 등장
    • HTML 문서와 상호작용이 가능하게 됨
    • 초창기엔 접근 가능한 태그가 많지 않았음
  • 표준안은 1998년에 등장했고, 대다수 브라우저에 적용되기까지 시간이 오래 걸림
  • 점차 발전하여 HTML 문서를 직접 수정까지도 가능하게 됨

DOM 선택

getElementById
- DOM Tree에서 요소 노드를 id로 찾음
- 제일 먼저 찾은 요소 하나를 반환

getElementByClassName
- DOM Tree에서 요소 노드를 class로 찾음
- 일치하는 모든 요소를 반환

getElementByTagName
- DOM Tree에서 요소 노드를 태그 이름으로 찾음
- 일치하는 모든 요소를 반환

querySelector
- DOM Tree에서 요소 노드를 CSS Selector 문법으로 찾음
- 제일 먼저 찾은 요소 하나를 반환

querySelectorAll
- DOM Tree에서 요소 노드를 CSS Selector 문법으로 찾음
- 일치하는 모든 요소를 반환

window.[id]
- id가 있는 요소는 window 객체를 통해 찾을 수 있음
- 여러 개라면 리스트로 반환

parentNode
- 선택한 요소 노드의 부모 노드를 불러옴
- document의 부모 노드는 null

firstElementNode
- 선택한 요소 노드의 자식 요소 노드 중 첫 번째를 불러옴
- 없을 경우 null을 반환

children
- 선택한 요소 노드의 자식 요소 노드를 불러옴
- 없을 경우 빈 배열을 반환

nextElementSibling
- 선택 요소 노드의 다음 형제 요소 노드를 불러옴
- 없을 경우 null을 반환

previousElementSibling
- 선택 요소 노드의 이전 형제 요소 노드를 불러옴
- 없을 경우 null을 반환


DOM 조작

class 접근
- 선택한 요소 노드에서 className과 classList로 요소의 class 속성을 불러오고 변경할 수 있음

hasAttribute
- 선택한 노드에서 속성을 가지고 있는지 확인할 수 있음

setAttribute
- 선택한 노드에서 속성을 정의

removeAttribute
- 선택한 요소 노드에서 속성을 제거

textContent
- 선택한 요소 노드에서 텍스트 노드에 접근, 변경할 수 있음

innerHTML
- 선택한 요소 노드 내부 HTML을 수정
- XSS 위험이 있음 (사용 추천Xx)

createElement
- 요소 노드를 (태그 이름으로) 생성할 수 있음

appendChild
- 선택한 요소 노드 마지막 자식 요소로 추가

removeChild
- 선택한 요소 노드 자식 노드 중 해당하는 요소를 제거



[7] Virtual DOM

virtual Dom이란?

한 번에 여러 개의 DOM 객체를 수정한다면?
-> DOM을 한번 조작할 때마다 다시 레이아웃을 잡고 렌더링 해야함
-> 만약, 한 번에 100개의 DOM을 수정해야한다면 최악의 경우 100번 렌더링을 해야할 수도 있음


Virtual DOM의 오해

virtual DOM은 DOM 보다 빠르다?

  • React 핵심 개발자 :
    • React는 DOM보다 빠르다는 것은 미신이고, 유지보수 용이한 애플리케이션을 만들도록 도와주고, 대부분의 유스케이스에 충분히 빠를 뿐


[8] DOM 조작 실습 - 간단한 에디터 만들어보기

-> 간단한 에디터 css적용한 모습

<!DOCTYPE html>
<html>
    <head>
        <title>Simple Editor</title>
        <link href="index.css" rel="stylesheet" type="text/css">
    </head>
    <body>
        <div class="toolbar">
            <button class="bold">Bold</button>
            <button class="italic">Italic</button>
            <button class="align-left">Align Left</button>
            <button class="align-center">Align Center</button>
            <button class="align-right">Align Right</button>
        </div>
        <!-- contenteditable : 이 영역 내에서 편집 가능 -->
        <div class="editor" contenteditable="true"></div>

        <script src="index.js"></script>
    </body>
</html>
.toolbar {
    width: 600px;
    height: 40px;
    padding: 8px;
    border-radius: 4px;
    background-color: bisque;
    box-sizing: border-box;
}

.toolbar button {
    height: 24px;
    border: 2px solid black;
    background-color: white;
    cursor: pointer;
}

.toolbar button:hover {
    background-color: gray;
}

.editor {
    width: 600px;
    height: 600px;
    padding: 16px;
    margin-top: 8px;
    font-size: 12px;
    border-radius: 4px;
    border: 2px solid black;
    outline: none;
    box-sizing: border-box;
}
// 즉시 실행 함수
(() => {
    document.querySelector(".bold").addEventListener("click", () => {
        document.execCommand("bold");
    });
    
    document.querySelector(".italic").addEventListener("click", () => {
        document.execCommand("italic");
    });

    document.querySelector(".align-left").addEventListener("click", () => {
        document.execCommand("justifyLeft");
    });
    document.querySelector(".align-center").addEventListener("click", () => {
        document.execCommand("justifyCenter");
    });
    document.querySelector(".align-right").addEventListener("click", () => {
        document.execCommand("justifyRight");
    });
})();

-> 보기가 너무 불편하고 하나로 묶어줄 수 있을 것 같음

<!DOCTYPE html>
<html>
    <head>
        <title>Simple Editor</title>
        <link href="index.css" rel="stylesheet" type="text/css">
    </head>
    <body>
        <div class="toolbar">
            <button data-command="bold">Bold</button>
            <button data-command="italic">Italic</button>
            <button data-command="justifyLeft">Align Left</button>
            <button data-command="justifyCenter">Align Center</button>
            <button data-command="justifyRight">Align Right</button>
        </div>
        <!-- contenteditable : 이 영역 내에서 편집 가능 -->
        <div class="editor" contenteditable="true"></div>

        <script src="index.js"></script>
    </body>
</html>
// 즉시 실행 함수
(() => {
    document.querySelectorAll('.toolbar button').forEach(element => {
        element.addEventListener('click', (e) => {
            const command = e.target.getAttribute('data-command');
            document.execCommand(command);
        });        
    });
})();
profile
안녕하세요

0개의 댓글