1일 1App 만들기 #8 [비유를 통한 이해]

하얀성·2023년 7월 13일
0

document.getElementById('play')는 'play'라는 아이디를 정확하게 가진 요소를 찾습니다. 그렇기 때문에 'play1'이라는 아이디를 가진 요소는 찾을 수 없습니다.

JavaScript는 대소문자 구분이 엄격하며, 아이디나 클래스 이름 등이 정확하게 일치해야 해당 요소를 찾을 수 있습니다. 따라서 'play'와 'play1'은 서로 다른 아이디로 취급됩니다.

예를 들어, 'play'라는 아이디를 찾으려면 document.getElementById('play')를 사용하고, 'play1'이라는 아이디를 찾으려면 document.getElementById('play1')를 사용해야 합니다.

-> id나 클래스의 명칭은 무조건 일치해야(부분집합 포함도 불가) 그 id나 class가 있는 요소를 가져온다.


playBtn.setAttribute('title', 'pause'); 코드는 playBtn 객체(HTML 요소)의 title 속성을 'pause'로 설정합니다. HTML에 이미 'title' 속성이 설정되어 있지 않더라도, setAttribute 메소드를 통해 'title' 속성을 추가하고 값을 설정할 수 있습니다.

다시 말해, playBtn 요소에 원래 title 속성이 없었다 해도 setAttribute를 통해 새로 'title' 속성을 만들고 'pause'라는 값을 부여합니다. 이 때문에 JavaScript에서는 이미 HTML에 존재하는 속성이 아닌 새로운 속성을 동적으로 추가할 수 있습니다.

다만, 제공된 HTML 코드에 playBtn 객체를 가져오는 getElementById('play') 라인에서 'play'라는 ID를 가진 요소가 없습니다. 따라서 이 부분이 수정되어야 합니다.

---

->요소변수명.setAtrribute('요소명', 요소의 값)

요소의 값을 바꾸거나 정의할 수 있다. 그래서 요소를 만드는데 사용

->document.getElementById('id명') 은 정확히 id명이 이미 선언된 id값과 일치해야 된다.


function loadMusic(song) {
    music.src = song.path;
    title.textContent = song.displayName;
    artist.textContent = song.artist;
    image.src = song.cover;
    background.src = song.cover;
}
function changeMusic(direction) {
    musicIndex = (musicIndex + direction + songs.length) % songs.length;
    loadMusic(songs[musicIndex]);
    playMusic();
}

changeMusic 함수는 direction 매개변수를 사용하여 현재 재생 중인 노래(musicIndex)를 변경합니다. direction 값이 양수이면 노래 리스트에서 다음 노래로, 음수이면 이전 노래로 이동합니다.

musicIndex = (musicIndex + direction + songs.length) % songs.length; 이 코드는 musicIndex를 변경하는데, 이는 노래 리스트의 인덱스를 나타냅니다. songs.length를 더하고 다시 songs.length로 나누는 이유는 배열의 범위를 벗어나지 않게 하기 위함입니다. 즉, 음수 인덱스나 배열의 길이를 초과하는 인덱스를 방지합니다.

그 다음, loadMusic(songs[musicIndex]); 코드로 변경된 musicIndex에 해당하는 노래를 로드하고, playMusic(); 함수로 노래를 재생합니다.

->

객체songs의 배열값들을 배열값들로 일일히 loadMusic()에서 song이라는 홀더에 연결되어 songs의 속성값들 path, artist... 등을 playMusic에서 사용하게 되었다.

<개발과정>

아.. 우리가 쉽게 알 수 있는 기본 기능을 함수로 구현하는 것에서부터 시작해서 이 함수를 구현할 때 필요한 다른 요소들을 구현하는 거구나.

기본 내용의 함수를 정의면서 이 기능이 필요하네 왜? 라고 끊임없이 만들어지는 기능을 깊게 깊게 파보면서 연결하는게 코드를 짜는 개발 과정이구나...

  1. 음악을 로딩해야 겠는데?
    각 음악 정보를 저장할 공간을 확보해야 겠는걸?
    공간확보 후 정보를 어떻게 불러오지?

  2. 아래에 정보를 불러올 함수를 만들자.
    함수를 만들어서 불러준다.

  3. 2번 함수와 1번함수를 1번함수의 매개변수를 통해 연결한다.

이런 함수를 정의하는 과정에서는
함수는 정보를 받아오고 저장하는 기능에 초점을 맞출수도 있고.
아니면 return을 통해 새로운 값을 만들어내는데 초점을 맞출수도 있고.


class Node{
  constructor(value){ 주술
    this.value = value; //주술 재료1
    this.next = null;   // 주술 재료2
  }
}

class Queue {
  constructor() { // 주술 의식
    this.head = null;
    this.tail = null;
    this.size = 0;
  }

  enqueue(newValue) {
    const newNode = new Node(newValue); 
    // 주술에 씌인 재료에 따라 각 다른 영혼이 계속 선택됨
    if(this.head === null) {
      this.head = this.tail = newNode; // 처음만든 영혼 가리키기
    }else {
      this.tail.next = newNode; // 새 영혼의 능력 사용. 
      this.tail = newNode; // 새 영혼 지목하기.
    }
    this.size += 1;
  }

다시 stack 코드가 헷갈려서 왔다. 결국 변수.속성이 왔다고 해서 우선순위가 후달리는 하부내용이라기보단, 근처의 속성을 아우른다는게 맞는듯하다.

그럼 Stack 알고리즘을 공부할 때에 Stack의 queue.tail.next 이런건 그럼 어떻게 되는가 궁금해져서 공부를 했다.

결론은 내가 상속과 인스턴스에 대해 단단히 착각하고 있었다.
인스턴스를 통해 그저 queue.tail과 인스턴스가 연결되서 인스턴스의 속성도 쓰게 되었던 것이지. 인스턴스가 queue.tail의 하부속성으로 들어가는게 아니었던 것이다. queue.tail = newNode 로서 새로운 노드가 되었고
queue.tail은 newNode로서 당당하게 자신의 권리를 주장하고 있는 것이었다.

---

클래스는 어떤 종류의 객체를 만들 것인지에 대한 '범주'나 '설계도'와 같습니다. 따라서 클래스를 '마법의 종류' 또는 '마법의 속성'이라고 생각하셔도 좋습니다.

예를 들어, "ShadowBeast"라는 클래스를 만든다면, 이 클래스는 '어둠계열'의 특성과 능력(methods)을 가진 '소환체'(인스턴스)를 만드는 데 사용될 수 있습니다. "ShadowBeast" 클래스로부터 만들어진 각각의 인스턴스는 '어둠계열' 소환체이며, "ShadowBeast" 클래스에서 정의된 특성과 능력을 가질 것입니다.

따라서 클래스를 '마법의 종류' 또는 '마법의 속성'이라고 생각하셔도 좋습니다. 클래스는 해당 범주에 속하는 객체를 생성하기 위한 기본 틀을 제공하며, 이 틀에 따라 실제 객체(인스턴스)가 만들어집니다.
클래스가 설계도라면, 인스턴스는 그 설계도에 따라 실체화된 개체입니다.

---

생성자(Constructor): '소환 주술'과 같은 특별한 주문. 이 주문을 통해 '영혼'(인스턴스)을 소환하고, 그에게 어떤 특성을 부여할지를 결정한다.

---

생성자를 사용하여 클래스 A의 인스턴스(A-1)를 만들 때, 이 A-1은 클래스 A의 "그림자 분신"이라고 생각할 수 있습니다. A-1은 A의 특성과 능력을 물려받지만, A-1 자체는 독립적인 존재입니다.

즉, A-1은 생성자를 통해 '소환'된 결과물이며, 그것은 원본 A와 동일한 특성을 가지지만, 독립적인 상태와 생명을 가지고 있습니다.

---

속성(Attributes): 소환된 '영혼'(인스턴스)의 특성 혹은 상태를 설명하는 정보. 이 속성들은 각각의 '영혼'(인스턴스)에 따라 다르게 설정될 수 있다.

Ex) 이 영혼은 ~~ 이름이고 ~~스킬을 쓸 수 있다.

매개변수(Parameters): '소환 주술'을 사용할 때 필요한 정보나 조건. 이 정보는 '영혼'(인스턴스)를 소환하고 설정하는 데 사용된다.

인스턴스라는 영혼. 그 영혼은 존재가 지옥에 있으며, 강령술로 그 힘을 잠시 빌릴 수 있다. 그 영혼은 영계에 속박되어 있다.

이것이 변수가 인스턴스를 가리킨다의 의미로 비유해보면 가장 가깝다고 하겠다.


변수는 생전 고인의 신체일부. 쓰던 머리카락, 피등의 매개물이라 보면된다. 그것을 통해 영혼을 잠시 불러내어 영혼의 능력을 쓸 수는 있되, 그 고인의 실체는 영계에 있다.'하지만' 영혼의 매개체이다. 영혼을 부르거나, 영혼에게 변화를 줄 수 있다는 것 뿐이지 영혼(데이터) 그 자체는 될 수 없다.

변수는 실제 데이터 값(또는 데이터 구조, 객체 등)의 "참조"나 "핸들"이라고 생각하실 수 있습니다. 이는 "생전 고인의 신체 일부"에 해당합니다. 변수는 데이터의 위치를 가리키는 방식으로 동작하여 프로그램에서 데이터를 접근하고 조작할 수 있는 방법을 제공합니다. 이는 "영혼을 잠시 불러내어 영혼의 능력을 쓸 수" 있는 부분에 해당합니다.

하지만 변수 자체가 데이터는 아니라는 점이 중요합니다. 즉, 변수는 단지 데이터를 참조하고 있을 뿐이며, 이 참조를 통해 데이터를 조작하거나 다른 데이터로 참조를 변경할 수 있습니다. 데이터의 실체는 컴퓨터 메모리 안 어딘가에 저장되어 있습니다. 이는 "고인의 실체는 영계에 있다"는 비유에 해당하겠습니다.

그리고 변수에 저장된 참조 값을 변경하면, 그 변수는 완전히 다른 데이터를 가리키게 됩니다. 이는 "영혼을 잠시 불러내어 다른 영혼으로 변경"하는 것과 비슷한 메커니즘입니다.

이런 방식으로, 변수를 이용하여 데이터에 접근하고 데이터를 조작하는 것이 가능해집니다. 이것이 변수가 프로그래밍에서 중요한 역할을 하는 이유입니다.

따라서

"함수(Function)"는 다양한 작업을 수행하는 '주술'의 한 형태입니다. 함수는 데이터를 가공하거나, 계산을 수행하거나, 다른 함수를 호출하는 등의 '일반 주술'로 생각할 수 있습니다. 이는 '소환술'(생성자)를 포함한 모든 종류의 주술을 수행할 수 있습니다.

따라서, '생성자'는 '함수'의 특수한 경우로, 특정 클래스의 인스턴스를 '소환'하는 역할을 합니다. 이러한 관점에서 볼 때, '생성자'는 '소환술'이며, '함수'는 '소환술'을 포함하는 모든 '주술'이라고 말할 수 있습니다.

---

이 비유는 Java와 같은 객체 지향 언어에서도 동일하게 적용될 수 있습니다.


unction changeMusic(direction){  
  musicIndex = (musicIndex + direction + songs.length) % songs.length; // 방향에 따라 index값을 변경.
  loadMusic(songs[musicIndex]);
  playMusic();
}

곡의 갯수는 추가되면 변하지 않고, 방향에 따라서 index의 값만 바꿔주는 코드.

배열 songs의 길이인 songs.length는 배열에 있는 요소의 수를 나타냅니다. 따라서 곡의 추가나 삭제가 없는 경우, songs.length는 변하지 않습니다.

위 코드에서 (3 - 1 + 3) % 3 계산은 (5) % 3으로 나타낼 수 있고, 이 값은 2가 됩니다.

만약 musicIndex가 3이고, direction이 -1이라면 (3 - 1 + 3) % 3 계산으로 musicIndex를 2로 설정합니다. 따라서 이것은 3번째 곡에서 이전 곡, 즉 두 번째 곡으로 이동하게 합니다.


function updateProgressBar(){
  const { duration, currentTime} = music;
  const progressPercent = (currentTime / duration) * 100;
  progress.style.width = `${progressPercent}%`;

  const formatTime = (time) => String(Math.floor(time)).padStart(2, '0');
  durationEl.textContent = `${duration / 60}:${formatTime(duration % 60)}`;
  currentTimeEl.textContent = `${currentTime / 60}:${formatTime(currentTime % 60)}`;
}

여기서 duration은 HTMLMediaElement의 속성 중 하나로, 오디오/비디오의 전체 길이(초 단위)를 나타냅니다. 이 값은 오디오/비디오의 메타데이터가 로드되면 사용할 수 있습니다.

코드에서 music는 Audio 객체의 인스턴스이며, 이 인스턴스는 오디오의 전체 길이(duration)과 현재 재생 위치(currentTime)를 속성으로 가집니다.

updateProgressBar 함수는 재생바를 업데이트 하는 기능을 수행합니다. currentTime은 현재 재생 중인 오디오의 시점을 초 단위로 나타내며, duration은 전체 오디오 파일의 길이를 초 단위로 나타냅니다.

progressPercent는 현재 재생된 비율을 백분율로 표현한 것입니다. 이 값을 이용하여 재생바의 너비를 설정하고 있습니다.

또한, formatTime 함수를 통해 duration과 currentTime을 분:초 형태로 바꿔서 화면에 표시하고 있습니다. padStart 메서드는 문자열의 시작을 다른 문자열로 채우는 역할을 합니다. 이 경우 2자리로 만들고, 부족한 경우 '0'으로 채웁니다. 따라서, 9초는 '09'초로 표현됩니다.

profile
기적을 한웅큼 품은 js러버

0개의 댓글