143 Matching Annotations
  1. Last 7 days
    1. app.METHOD(PATH, HANDLER)

      Client │ GET /api/movies ▼

      Router │ "이 요청은 getMovies가 처리해!" ▼

      Controller: getMovies() │ Movie.find() 호출 ▼

      Model → MongoDB

      // Controller 함수

      async function getMovies(req, res) {

      const movies = await Movie.find();

      res.json(movies);

      }

      // Routing 설정 app.get("/api/movies", getMovies);

    2. Middleware parse JSON, log|vRouter method + URL|vController does the work

      Express = HTTP 요청을 받아 Middleware, Router, Controller로 처리하는 서버 프레임워크

    3. GraphQL (Facebook, 2015)

      REST는 Resource에 따라 여러 Endpoint를 두는 방식이고, GraphQL은 보통 하나의 Endpoint에 서로 다른 Query를 보내 원하는 데이터를 지정하는 방식

    4. URL (path)

      ① Client가 요청을 보냄

      GET /api/movies/42?genre=Drama │ │ │ └─ Query String └─ URL Path

                   ↓
      

      ② Express Controller가 값 추출

      req.params.id → "42" req.query.genre → "Drama"

                   ↓
      

      ③ Controller가 Mongoose 함수 호출

      Movie.find({ genre: "Drama" })

                   ↓
      

      ④ MongoDB에서 실제 데이터 검색

                   ↓
      

      ⑤ Controller가 Client에게 JSON 응답

    Annotators

    1. new Schema( {...}, {...} )

      구분 required: true timestamps: true

      역할 특정 필드의 필수 여부 설정 날짜 자동 관리 기능 설정

      적용 대상 개별 Field Schema 전체

      코드 위치 첫 번째 인자 안 두 번째 인자 안

      의미 이 필드가 필요해! 생성·수정 시간을 관리해!

    2. Model

      Model은 schema에서 제공해준 형태의 Document 하나가 아니라,

      Document를 생성·조회·관리하는 기능을 제공한다고 이해하면 돼.

    3. Document(embedded/nested document)

      MongoDB가 _id를 자동으로 붙여 주는 대상은 Collection의 최상위 Document이고,

      그 안에 들어 있는 Embedded Document에는 자동으로 붙이지 않아.

    4. 12 bytes

      언제, 어느 기계에서, 어떤 프로세스가 만들었는지와 카운터 값을 조합해 ID가 겹치지 않도록 설계한 것이야.

    Annotators

    1. MappingAPI
      • Mapping API = 프로그램의 데이터(객체)와 데이터베이스의 데이터를 연결해서 다루기 쉽게 해주는 도구
    2. if (document.cookie.split(';').filter(function(item) {return item.indexOf('username=') >= 0}).length)

      // ① 쿠키 문자열을 배열로 나눔 const cookies = document.cookie.split(';');

      // ② username=이 포함된 항목만 남김 const matches = cookies.filter(function(item) { return item.indexOf('username=') >= 0; });

      // ③ 남은 항목 개수 확인 const count = matches.length;

      // ④ 하나 이상 있으면 출력 if (count) { console.log('A username cookie was found'); }

    3. myCookie.replace(regex, "$1")

      let text = "username=Joe";

      let result = text.replace( /username=(.*)/, "$1" );

      console.log(result); // "Joe"

      정규표현식을 분해하면: username=(.*) └─┘ ↑ 첫 번째 그룹

      • username= → 이 글자를 찾음
      • (.*) → 그 뒤의 문자열을 첫 번째 그룹으로 잡음
      • "$1" → 첫 번째 그룹에 잡힌 문자열로 교체
    4. encryption

      원본 메시지: Hello Joe Encryption + Key

      암호화된 데이터 읽을 수 없는 형태의 바이트 데이터

      Decryption + Key

      복호화된 메시지: Hello Joe

    Annotators

    1. Decides how long the state lasts:app 전체를 감싸면: 전체 session 동안 유지한 화면(screen)만 감싸면: 그 화면을 떠나면 사라진다.

      Provider Component가 useState()로 State를 관리하고 있다면, 그 Provider Component가 Mount된 상태인 동안 State가 유지된다는 뜻이야.

    2. Decides who can see the state: provider 안에 있는 component만 접근할 수 있다

      ① Decides who can see the state

      Provider가 감싸는 범위에 있는 하위 컴포넌트만 그 Provider가 제공한 Context 값을 읽을 수 있다는 뜻이야.

      Provider 밖의 컴포넌트가 useContext()를 호출하면 해당 Provider의 값을 읽지 못하고, 다른 상위 Provider가 없다면 createContext(defaultValue)의 기본값을 사용해.

    3. () =>

      Lazy Initialization 기억나?

      초기값을 계산하는 함수를 useState에 전달해서 처음 State를 만들 때만 그 계산을 하도록 하는 방식이야.

    4. []

      ③ ?? []

      이것도 새로운 문법이지? 값 ?? 기본값

      은 왼쪽 값이 null 또는 undefined이면 오른쪽 값을 사용한다는 뜻이야.

    5. localStorage.getItem('roster')

      ① localStorage.getItem('roster') localStorage는 브라우저가 데이터를 저장할 수 있는 공간이야.

      예를 들어 예전에 학생 목록을 저장했다고 해 보자. roster → '[{"id":"1","name":"Joe","year":2026}]'

      localStorage.getItem('roster')는 이 저장된 문자열을 가져오는 거야. 저장된 값이 없으면 null을 반환해.

    6. 2. Provider component

      function ThemeProvider({ children }) { const [theme, setTheme] = useState('light');

      return ( <ThemeContext.Provider value={theme}>

        {children}
      
      ```</ThemeContext.Provider>```
      

      ); }

    7. : component가 읽는 context value가 바뀌었을 때. props가 바뀌지 않았어도 re-render된다

      어떤 컴포넌트가 useContext()로 Context 값을 사용하고 있다면,

      Props가 그대로여도 해당 Context 값이 바뀌었을 때 그 컴포넌트가 다시 렌더링된다.

    8. context는 state variable 묶음에 대한 key다

      const UserContext = createContext(null);

      function App() {

      const [name, setName] = useState("Joe"); const [age, setAge] = useState(20);

      return ( <UserContext.Provider value={{ name, age, setName }}>

        <Profile />
      
      ```</UserContext.Provider>```
      

      ); }

      여기서 두 종류의 {}가 등장해. value={{ name, age, setName }}

      • 바깥 {} → JSX에서 JavaScript 표현식을 전달

      • 안쪽 {} → name, age, setName이 들어 있는 객체 생성

    9. paint한 뒤에

      ① Render Clock() 함수 실행, JSX 계산

      ② DOM Update 화면의 시간 텍스트 변경

      ③ Browser Paint 변경된 화면을 실제로 그림

      ④ useEffect 실행 타이머 설정 또는 브라우저 제목 갱

    10. effect 하나가 method 한 쌍을 대체한다

      예전 Class에서는 브라우저 제목을 바꾸려고 두 번 작성해야 했어. componentDidMount() { document.title = this.state.now.toLocaleTimeString(); }

      componentDidUpdate() { document.title = this.state.now.toLocaleTimeString(); }

      왜? - 처음 Mount했을 때도 제목을 설정해야 하고 - 시간이 바뀔 때도 제목을 수정해야 하니까 그런데 Hooks에서는: useEffect(() => { document.title = now.toLocaleTimeString(); }, [now]);

      이 코드 한 번으로 두 상황을 처리할 수 있어.

    11. [stateVar]

      function Clock() { const [now, setNow] = useState(new Date());

      // 타이머 담당

      useEffect(() => { const timerId = setInterval(() => { setNow(new Date()); }, 1000);

      return () => clearInterval(timerId);
      

      }, []);

      // 브라우저 제목 담당

      useEffect(() => { document.title = now.toLocaleTimeString(); }, [now]);

      return

      {now.toLocaleTimeString()}

      ; }

    12. code가 timing이 아니라 purpose 기준으로 묶인다

      function Clock() { const [now, setNow] = useState(new Date());

      // 타이머 담당 useEffect(() => { const timerId = setInterval(() => { setNow(new Date()); }, 1000);

      return () => clearInterval(timerId);
      

      }, []);

      // 브라우저 제목 담당 useEffect(() => { document.title = now.toLocaleTimeString(); }, [now]);

      return

      {now.toLocaleTimeString()}

      ; }

    13. 다시 실행되기 전

      ① State 변경 → Update 요청 ↓ ② React가 컴포넌트 함수 다시 호출 (Render) ↓ ③ React가 DOM 업데이트 ↓ ④ 이전 Effect의 Cleanup 실행 (필요하다면) ↓ ⑤ 새로운 Effect 실행

      즉, Cleanup은 Render 중에 실행되는 게 아니야!

    14. cleanup,

      useEffect(() => { console.log("Effect 실행:", now);

      return () => { console.log("Cleanup 실행:", now); }; }, [now]);

      now가 바뀌면 실행 순서는 다음과 같아. 시점 실행 순서 처음 Mount Effect 실행: 12:00:00

      1초 후 Update Cleanup 실행: 12:00:00 → Effect 실행: 12:00:01

      다시 1초 후 Update Cleanup 실행: 12:00:01 → Effect 실행: 12:00:02

      Unmount Cleanup 실행: 12:00:02

    15. return () => clearInterval(timerId);

      ide 클릭 ↓ visible = false ↓ Clock Unmount ↓ 첫 번째 Effect Cleanup 실행 ↓ clearInterval(timerId) ↓ 타이머 중지

    16. useState(() => loadFromStorage())

      첫 번째 Render (Mount) → React가 초기화 함수 실행 → loadFromStorage() 호출 → 100을 초기 State로 저장

      두 번째 Render (Update) → 기존 State 사용 → loadFromStorage() 실행하지 않음

      세 번째 Render (Update) → 기존 State 사용 → loadFromStorage() 실행하지 않음

    17. useState(loadFromStorage())

      첫 번째 Render → loadFromStorage() 실행 → useState(100)

      두 번째 Render → loadFromStorage() 또 실행 → useState(100)

      세 번째 Render → loadFromStorage() 또 실행 → useState(100)

      React는 초기값 100을 첫 Mount에서만 사용하지만, JavaScript는 렌더링할 때마다 loadFromStorage()를 실행해 버려. 즉, 불필요한 계산이 반복되는 문제가 있어.

    18. React가 넘겨주는 latest value 사용. stale 문제 없음

      이전 State를 바탕으로 새 State를 계산할 때는 setCount(c => c + 1)을 사용하면 오래된 렌더링의 count 값에 의존하지 않아.

    19. 각 hook call은 그 list의 다음 slot을 읽거나 쓴다

      React는 Hook 호출 순서로 저장 칸을 찾기 때문에, 매번 Render할 때 Hook이 같은 순서로 실행되어야 한다.

    20. click handler나 timer에서 useState를호출하면 error다.

      function Counter() {

      function handleClick() {

      const [count, setCount] = useState(0); // ❌
      

      }

      return <button onClick={handleClick}>Click</button>; }

    Annotators

    1. this.timerId

      this.timerId = 5; // ✅ 일반 인스턴스 속성 변경 this.state.now = new Date(); // ❌ State 직접 수정 this.setState({ now: new Date() }); // ✅ State 업데이트

      핵심은 this.를 붙였다고 전부 State가 되는 건 아니라는 거야!

      • this.timerId → Clock 객체가 직접 가진 속성
      • this.state → Clock 객체가 가진 State 객체
      • this.state.now → State 객체 안에 있는 now 속성

      한 줄 암기: state에 없어도 this.속성이름 = 값으로 인스턴스 속성을 새로 만들 수 있다. 단, React 화면에 영향을 주는 State는 setState()로 변경해야 한다.

    2. setInterval

      setInterval()은 정해진 시간 간격으로 함수를 반복 실행하는 JavaScript 함수

      setInterval(실행할함수, 시간간격);

    3. Updating

      Updating — State / Props가 변경될 때

      shouldComponentUpdate()

      render()

      getSnapshotBeforeUpdate()

      DOM Update

      componentDidUpdate()

    4. hook method

      React가 특정 Lifecycle 시점에 호출하는 메서드

      메서드 역할 render() 현재 Props/State로 UI 계산

      componentDidMount() 처음 DOM에 추가된 후 작업

      componentDidUpdate() 업데이트가 반영된 후 작업

      componentWillUnmount() DOM에서 제거되기 전 정리

    5. Click →

      StudentRow에서 Jane 클릭

      onSelect({selectedId}) 호출

      App.setState({{selectedId}}) 요청

      React가 App.render() 재실행

      Roster와 StudentRow 렌더링

      selected 결과에 따라 > 표시 변경

    6. <section>

      <section>은 <h2>와 <ul>을 하나의 Root Element로 묶기 위해 사용한 거야.

      반드시 <section>일 필요는 없으며 <div>나 Fragment로 대체할 수도 있어.

    7. const { title, students, selectedId, onSelect } = this.props;

      destructing -> 굳이 this.props.속성 이렇게 사용 안 해도 됨

      const title = this.props.title;

      const students = this.props.students;

      const selectedId = this.props.selectedId;

      const onSelect = this.props.onSelect;

    8. State

      시험에서 가장 헷갈리는 중괄호 정리 코드 {} 의미 state = { … } State 객체 생성

      { id: 1, name: 'Joe' } 학생 객체 생성

      (id) => { … } 함수 본문

      this.setState({ selectedId: id }) 변경할 state 속성을 객체로 전달

      students={this.state.students} JSX에서 JavaScript 값 사용

      onSelect={this.handleSelectStudent} JSX에서 함수 객체 전달

    9. destructure

      function Greeting({ name })은 function Greeting(props)에서 props.name을 일일이 쓰지 않도록 name을 바로 꺼내는 축약 문법이야

    10. B. Class-based vs Functional Components (S6–S8)

      비교 Class-based Functional

      정의 방식 class Greeting extends Component function Greeting(props)

      React 상속 필요 필요 없음

      JSX 반환 render() 안에서 함수에서 직접

      Props 접근 this.props.name props.name 사용 방법 <Greeting name="Joe" /> <Greeting name="Joe" />

    11. jsx

      여기서 jsx()는 React의 JSX 변환 과정에서 사용하는 함수야. 쉽게 다음 형태로 이해하면 돼.

      jsx(태그이름, { 속성들, children: 자식내용 });

    12. 안에 any JavaScript expression 사용 가능

      일반 JavaScript에서는 평소처럼 코드를 쓰고, JSX 내부에서 JavaScript 값을 끼워 넣을 때만 {}를 사용하면 돼!

      코드 {} 의미 function App() { ... } 함수 본문을 감싸는 중괄호

      <h1>{name}</h1> JSX 안에서 JS 표현식 사용

      onClick={() => alert("Hi")} JSX 속성에 JS 함수 전달

    13. build tool이 각 <tag> 를 function call로 compile

      JSX는 개발자가 편하게 작성하는 문법이고, Build Tool은 이를 브라우저에서 실행할 수 있는 JavaScript로 번역해 주는 거야.

    Annotators

    1. B. Class-based vs Functional Components (S6–S8)

      비교 Class-based Functional

      정의 방식 class Greeting extends Component function Greeting(props)

      React 상속 필요 필요 없음

      JSX 반환 render() 안에서 함수에서 직접

      Props 접근 this.props.name props.name 사용 방법 <Greeting name="Joe" /> <Greeting name="Joe" />

    2. Update는 즉시 일어나지 않는다

      console.log(this.state.selectedId); // null

      this.setState({ selectedId: 2 });

      console.log(this.state.selectedId); // ?

      강의자료의 설명에 따르면 두 번째 console.log()도 null이야.

      왜냐하면 setState()는 변경을 즉시 완료하는 게 아니라, React에 State 업데이트를 예약(schedule)하기 때문이야.

      실행 흐름 (슬라이드 기준) 1. 현재 selectedId = null 2. setState({ selectedId: 2 }) 요청 3. 바로 다음 줄에서는 여전히 이전 값 null을 읽음 4. React가 State 변경을 처리 5. 새 State를 바탕으로 다시 Render

    3. never assign to it directly

      JavaScript 객체의 값은 바뀔 수 있지만, React는 이 대입만으로 화면을 다시 렌더링해야 한다는 사실을 알지 못해.

      this.state.selectedId = 2;

    4. component lifecycle functions

      class App extends Component {

      componentDidMount() {

      // 컴포넌트가 DOM에 추가된 후 실행
      // Data fetch, timer 시작 등
      

      }

      render() {

      // 화면에 보여줄 JSX 계산
      ```return <h1>Hello!</h1>;```
      

      } }

    5. Click

      StudentRow에서 Jane 클릭

      onSelect({selectedId}) 호출

      App.setState({{selectedId}}) 요청

      React가 App.render() 재실행

      Roster와 StudentRow 렌더링

      selected 결과에 따라 > 표시 변경

    6. <section>

      <section>은 <h2>와 <ul>을 하나의 Root Element로 묶기 위해 사용한 거야.

      반드시 <section>일 필요는 없으며 <div>나 Fragment로 대체할 수도 있어.

    7. const { title, students, selectedId, onSelect } = this.props;

      destructing -> 굳이 this.props.속성 이렇게 사용 안 해도 됨

      const title = this.props.title;

      const students = this.props.students;

      const selectedId = this.props.selectedId;

      const onSelect = this.props.onSelect;

    8. destructure

      function Greeting({ name })은 function Greeting(props)에서 props.name을 일일이 쓰지 않도록 name을 바로 꺼내는 축약 문법이야

    9. State
      1. 시험에서 가장 헷갈리는 중괄호 정리 코드 {} 의미

      state = { ... } State 객체 생성

      { id: 1, name: 'Joe' } 학생 객체 생성

      (id) => { ... } 함수 본문

      this.setState({ selectedId: id }) 변경할 state 속성을 객체로 전달

      students={this.state.students} JSX에서 JavaScript 값 사용

      onSelect={this.handleSelectStudent} JSX에서 함수 객체 전달

    10. jsx

      여기서 jsx()는 React의 JSX 변환 과정에서 사용하는 함수야. 쉽게 다음 형태로 이해하면 돼.

      jsx(태그이름, { 속성들, children: 자식내용 });

    11. 안에 any JavaScript expression 사용 가능

      일반 JavaScript에서는 평소처럼 코드를 쓰고, JSX 내부에서 JavaScript 값을 끼워 넣을 때만 {}를 사용하면 돼!

      코드 {} 의미 function App() { ... } 함수 본문을 감싸는 중괄호

      <h1>{name}</h1> JSX 안에서 JS 표현식 사용

      onClick={() => alert("Hi")} JSX 속성에 JS 함수 전달

    12. build tool이 각 <tag> 를 function call로 compile

      JSX는 개발자가 편하게 작성하는 문법이고, Build Tool은 이를 브라우저에서 실행할 수 있는 JavaScript로 번역해 주는 거야.

    Annotators

    1. Getting Nodes

      HTML에 이미 존재하는 요소를 JavaScript에서 가져오는 거야.

      <div id="container">

      Hello

      World

      </div>

      코드 기능 결과 document.getElementById("container") id로 찾기 요소 하나 또는 null

      document.getElementsByClassName("text") class로 찾기 HTMLCollection

      document.querySelector(".text") CSS 선택자로 찾기 첫 번째 일치 요소

      document.querySelectorAll(".text") CSS 선택자로 전부 찾기 NodeList

    2. “Also beware, inside an event handler function, this may not be what you think it is.” (Slide 11에서도 반복된 강조)

      obj.method() → this = obj

      element.addEventListener("click", obj.method) → 일반 함수라면 클릭 시 this = element

      element.addEventListener("click", () => obj.method()) → method 내부에서 this = obj

    3. “Always be aware of what type an element is.” (교수님 강조)

      기능 Native DOM (a) jQuery (b)

      내용 변경 a.innerHTML = "Hi" b.html("Hi")

      이벤트 등록 a.addEventListener("click", foo) b.on("click", foo)

      CSS 변경 a.style.color = "red" b.css("color", "red")

      요소 삭제 a.remove() b.remove()

    4. Event Handling

      방법 A. addEventListener() button.addEventListener("click", callback);

      방법 B. onclick 속성 button.onclick = callback;

      방법 C. HTML에 이벤트 지정 <button onclick="callback()">Click</button>

    5. Slide 13: 응답 가능한 Events (전체 목록)

      4-2. 이벤트 종류 (Slide 13) 이벤트 언제 발생? click 클릭 load 로딩 완료 unload 문서에서 벗어나는 과정 change 값 변경이 확정될 때 mouseover 마우스가 요소 안으로 들어올 때 mouseout 마우스가 요소 밖으로 나갈 때 mousedown 마우스 버튼 누름 mouseup 마우스 버튼 뗌

    6. $.getJSON(jsonFilePath, function (json) {

      $.getJSON()은 jQuery에서 JSON 데이터를 비동기적으로 가져오는 메서드야.

      jsonFilePath는 파일 경로.

      function(json) { ... }는 데이터를 성공적으로 가져왔을 때 실행할 Callback이야.

      json에는 가져온 JSON 데이터가 들어와.

    7. Slide 5: Make new nodes 

      document.createElement("div") 새로운 div 생성 X document.createTextNode("Hi") 텍스트 노드 생성 X parent.appendChild(child) 부모에게 자식 추가 O parent.removeChild(child) 부모에게서 자식 삭제 O child.remove() 자기 자신 삭제 X child.parentNode 부모 노드 참조 X

    8. lide 4: Updating the DOM

      코드 종류 하는 일 element.innerHTML = "Hi" 속성 HTML 내용 수정 element.textContent = "Hi" 속성 텍스트 내용 수정 element.setAttribute("id", "box") 메서드 HTML 속성 지정 element.style.color = "red" 속성 글자 색상 수정 element.classList.add("active") 메서드 class 추가

    9. control.addEventListener("click", controller.processGoHome);

      element.addEventListener("이벤트 종류", 실행할 함수);

      부분 의미 element 이벤트를 감지할 HTML 요소 addEventListener 이벤트 리스너 등록 "click" 어떤 이벤트를 기다릴지 실행할 함수 이벤트 발생 시 실행할 Callback

      ++++ control.addEventListener("click", controller.processGoHome());-> totally different = exectues as soon as it is regeistered not clicked

    10. child를 지우는 건 parent의 일)

      // ① 삭제할 자식 가져오기 let myParagraph = document.getElementById("my_paragraph");

      // ② 부모 노드 가져오기 let parent = myParagraph.parentNode;

      // ③ 부모가 자식 삭제하기 parent.removeChild(myParagraph);

    1. scope 규칙

      let은 우리가 상식적으로 기대하는 블록 스코프(Block scope)를 엄격히 강제(enforce)하여 변수의 생명주기를 안전하게 가둠

    2. window.main = main; // 인라인 classic script 에서 보이게 노출

      앞서 살펴본 type="module" 내부에서 선언된 모든 변수, 함수, 클래스는 파일 단위의 독자적인 스코프를 갖기 때문에 window 객체에 전혀 등록되지 않습니다. 그러므로 따로 여기서 등록할 경우 말이 됨

    3. 2. How it works

      만약 스크립트가 <head>나 <body> 윗부분에 위치하고, 그 스크립트 안에 다음과 같은 전역 코드가 있다면:

      JavaScript // external.js (head에서 로드됨) const btn = document.getElementById("submit-btn"); btn.addEventListener("click", () => { ... }); 브라우저는 아직 <body> 아래쪽에 적힌 <button id="submit-btn"> 태그를 읽지도 못한 상태입니다.

      그런데 중간에 스크립트를 만나 실행부터 해버리므로 document.getElementById("submit-btn")은 null을 반환합니다.

      결국 null.addEventListener(...)가 되어 TypeError (Cannot read properties of null)가 발생합니다.

    4. C-1. 함수는 head에 둘 수 있지만 .js에 두는 게 좋다 (S10)

      "단순 함수 정의라면 실행되지 않고 등록만 되니 head에 두어도 에러 없이 동작할 수는 있으나(can), 실제로는 성능(head 안의 스크립트를 만나는 순간 JS를 처리하므로, 코드가 길어지며 첫 화면 속도 저하)·캐싱·유지보수를 위해 별도 외부 .js 파일로 분리하는 것이 정석(should)"

    5. awful

      〈script src="utils.js"〉〈/script〉 〈script src="models.js"〉〈/script〉 〈script src="api.js"〉〈/script〉 〈script src="ui.js"〉〈/script〉 〈script src="main.js"〉〈/script〉

    6. <script src="myScript.js"></script>
      1. 주요 사용 방식

      • 인라인 스크립트 (Inline Script): 태그 사이에 자바스크립트 코드를 직접 작성합니다.html

      <script> alert("안녕하세요!"); </script>

      • 외부 파일 불러오기 (External Script): src 속성에 자바스크립트 파일(.js) 경로를 지정합니다.html

      <script src="script.js"></script>

      (주의: src 속성이 있는 <script> 태그 내부에는 자바스크립트 코드를 직접 작성하면 안 됩니다.)

    1. 컨트롤러가 한 번 만들어서 넘기는 이유

      처음 추가: Buy milk id = 123

      redo 후: Buy milk id = 456

      doTransaction() 안에서 new ListItem()을 하면 redo할 때마다 새 아이템이 생기기 때문에, 컨트롤러가 아이템을 한 번만 만들고 transaction이 그 같은 객체를 계속 들고 있어야 한다.

    2. const action = actionButton?.dataset.action ?? 'edit-item'; // ⑤ 버튼이면 그 종류, 아니면 'edit-item'

      let action;

      if (actionButton !== null && actionButton !== undefined) { action = actionButton.dataset.action; } else { action = 'edit-item'; }

    3. const card = target.closest('.item-card'); // ① 클릭된 곳에서 위로 올라가며 카드를 찾음

      button부터 시작 │ ├─ button 자신이 .item-card인가? │ → 아니야 │ ↑ 부모로 올라감 │ └─ li가 .item-card인가? → 맞아! → 이 li를 반환

    4. 5-7. "HTML을 JS에서 만들지 않는다" 규칙

      textContent = "이건 글자야." innerHTML = "이걸 HTML 코드로 해석해."

      그리고 너희 과제에서는: 사용자가 입력한 데이터 → 글자로 보여주고 싶음 → textContent

    5. pill.className = `priority-pill ${Priority.toCssClass(item.priority)}`; // 클래스 통째로 지정

      <span class="priority-pill"> High </span> -> <span class="priority-pill priority-high"> High </span>

    6. domEvent

      domEvent ├── type: "click" ├── target: 실제 클릭한 요소 ├── currentTarget: 리스너가 달린 요소 ├── clientX: 마우스 X 위치 ├── clientY: 마우스 Y 위치 └── ...

    7. 이벤트 버블링

      클릭 이벤트가 버블링해서 cardContainer까지 올라왔을 때, 그 클릭에 대한 정보(domEvent)를 받아서 실제 클릭한 요소(domEvent.target)를 확인한다.

    8. CardPrototype.requirePart(element, '.item-description').

      element 안에서 .item-description인 부분을 찾아줘 즉 이걸 찾는 거지: <span class="item-description"></span>

    9. element.dataset.itemId = item.id; // data-item-id="item-3f9a..." element.dataset.index = String(index); // data-index="2"

      HTML 요소 자체에 숨은 정보표를 붙이는 거야. 결과적으로 개념상 이렇게 돼.

    10. 즉 카드가 스스로: 나는 item abc123이고, 2번째 item이야. 라고 기억하게 하는 거야.

    11. 5-2. ListView.render(): 그리기의 중심 

      render() → iterator로 item 하나 가져옴 → cardPrototype.clone(item, index) → clone() 안에서 prototype element 확보 → 그 prototype을 cloneNode(true)로 복사 → initializeClone()으로 실제 데이터 넣기 → 완성된 카드를 fragment에 추가 → 모든 카드가 모이면 replaceChildren(fragment) → 화면이 현재 model 상태에 맞게 다시 그려짐