Hacker News Client app 01 : JavaScript

gyomni·2022년 1월 13일
0

Week I Learned

목록 보기
6/20
post-thumbnail

👏 Hacker News API를 받아와서 JS로 클라이언트 앱을 만들어 보며 학습해보자!!

1) API를 받아와서 title 데이터 리스트에 넣어보기.

// 서버 (네트워크 너머에 있는 데이터) 가져오는 도구 = ajax 
const ajax = new XMLHttpRequest(); // new XMLHttpRequest()가 반환하는 값을 저장하는 저장소 만들어주기.
// ajax라는 변수 통해서 XMLHttpRequest가 제공하는 도구를 사용할 수 있음.

const NEWS_URL = 'https://api.hnpwa.com/v0/news/1.json'; // 바뀔 가능성이 있는 데이터는 따로 변수로 빼주는 것이 좋음. 
    
// 데이터 가져오기
ajax.open('GET', NEWS_URL, false); // false : 가져오는 데이터를 동기적으로 가져오겠다(처리하겠다)는 뜻 
ajax.send(); // open만 한다고 해서 데이터 가져오는 것은 아니고, send라는 함수 호출하면 데이터 가져오게 됨.


// 데이터 처리
// ajax.response; : 데이터 어디 들어왔는지 확인 -> response라는 값에 들어있음.
// response에 있는 데이터를 js에서 다루기 쉽도록 Preview탭처럼(= 객체 ) 바꾸기. 즉 응답값을 객체로 바꾸기 
//  -> 어떤 응답 값이는 다 객체로 바꿀 수 있는 것은 아니고, 응답 값으로 온 형식이 객체로 바꿀 수 있는 형식인 json형식이기 때문에 가능한 것.
// console.log(ajax.response); // 콘솔로 찍어보고 확인!

// jSON데이터를 객체로 바꾸기
// 반환한 객체를 변수로 받아주기, JSON.parse() : 괄호 안에 입력으로 받은 JSON데이터를 객체로 바꿔서 반환해줌.
const newsFeed = JSON.parse(ajax.response);
// console.log(newFeed); // 콘솔로 찍어보고 확인!

const ul = document.createElement('ul'); // ul태그 생성, document라고 하는 도구가 바로 HTML을 조작하는 데 필요한 모든 도구들을 제공해 주는 도구.


for (let i = 0; i < newsFeed.length; i++) {
    const li= document.createElement('li'); // li는 매번 새로 만들어 지기 때문에 for문안에 만들어주기.
    li.innerHTML=newsFeed[i].title;
  
  	ul.appendChild(li);
}

document.getElementById('root').appendChild(ul); // ul 태그 만든 것을 root 하위에 넣어주기. appendChild : 자식추가


현재 newsFeed길이만큼 리스트를 받아오는 형태로 화면에 출력된다 ~!

2) 댓글 수 받아오고, title에 링크걸어서 클릭하면 화면에 출력되게!

const container = document.getElementById('root');
const ajax = new XMLHttpRequest();

const content = document.createElement('div');
const NEWS_URL = 'https://api.hnpwa.com/v0/news/1.json'; 
const CONTENT_URL='https://api.hnpwa.com/v0/item/@id.json'; // id값 마킹하기 위해 @사용(방법 여러가지 있음.)

    
// 데이터 가져오기
ajax.open('GET', NEWS_URL, false);
ajax.send(); 


// 데이터 처리
const newsFeed = JSON.parse(ajax.response);
const ul = document.createElement('ul'); .

// 해시체인지
// 해시체인지가 발생했을 때 함수가 호출됨.
// 각각의 것들의 내용이 실제로 바뀌어야 해시체인지가 일어남.
// 해시 체인지 안에 있는 함수가 각각의 글들이 한번씩 클릭 될 때마다 호출되는 상황 만들기.
window.addEventListener('hashchange',function(){
    // hashchange일어났을 때 id 알수 있는 방법-> location이라는 객체로!(주소와 관련된 다양한 정보 제공) 해시는 주소에 붙어있음.  ==> location 객체에 해시라고 하는 속성으로 데이터를 넘겨줌.
    const id =location.hash.substr(1); // substr : 내가 쓰고 싶은 위치 값만 지정해 주면 그 이후부터의 값까지 씀. 시작 값은 0부터 씀 , console.log(location.hash)로 찍어보면 #아이디값 이 출력되서 #을 제외해줘야 하기때문에 1이라고 입력! 
                                       // 첫번째 위치부터 나머지 뒤에 있는 문자열들만 반환.
  
// CONTENT_URL에 @id를 위에서 생성한 id로 바꿔주기. replace : 값을 대체해 주는 문자열 함수
  	ajax.open('GET', CONTENT_URL.replace('@id',id), false);
	ajax.send(); 
    
    const newsContent = JSON.parse(ajax.response);
    const title = document.createElement('h1');

    title.innerHTML=newsContent.title;
    
    content.appendChild(title);
    
});

for (let i = 0; i < newsFeed.length; i++) {
    const li = document.createElement('li'); 
    const a = document.createElement('a'); 
  
  	a.href=`#${newsFeed[i].id}`;
  	a.innerHTML=`${newsFeed[i].title}(${newsFeed[i].commnets_count})`;
  
   
  	li.appendChild(a);
  	ul.appendChild(li);
}
container.appendChild(ul); // ul 태그 만든 것을 root하위에 넣어 주기. appendChild: 자식 추가 
container.appendChild(content);

url창을 보면! 링크 클릭할 때마다 id값 다르게 노출됨을 알 수 있음!

🚨 🚨 🚨
단, 현재는 title링크를 클릭 하는 대로 계속 화면에 title이 이어서 출력된다..!
또한, DOM API를 이용해서 UI를 만든 탓에 UI가 어떻게 생겼는지 잘 파악이 안됨.

3) 리펙토링! UI는 바뀐게 없지만 함수를 사용해서 코드 반복을 막자!

const container = document.getElementById('root');

const ajax = new XMLHttpRequest(); 
const content = document.createElement('div');
const NEWS_URL = 'https://api.hnpwa.com/v0/news/1.json';  
const CONTENT_URL='https://api.hnpwa.com/v0/item/@id.json';

function getData(url){
    
    // 데이터 가져오기
    ajax.open('GET', url, false); 
    ajax.send(); 
    return JSON.parse(ajax.response); 

}
 
const newsFeed = getData(NEWS_URL);

const ul = document.createElement('ul'); 

window.addEventListener('hashchange',function(){
    const id =location.hash.substr(1);
    const newsContent = getData(CONTENT_URL.replace('@id',id));
  
    const title = document.createElement('h1');

    title.innerHTML=newsContent.title;
    
    content.appendChild(title);
    
});


for (let i = 0; i < newsFeed.length; i++) {
    const div = document.createElement('div');
    const li= document.createElement('li'); 
    const a = document.createElement('a');

    div.innerHTML=`
    <li>
        <a href="#${newsFeed[i].id}">
        ${newsFeed[i].title} (${newsFeed[i].comments_count})
        </a>
    </li>`

    ul.appendChild(div.firstElementChild); //  = ul.appendChild(div.children[0]); : 첫번째 자식요소 가져오기.
}
container.appendChild(ul);
container.appendChild(content);

1. getData 함수를 생성해서 코드 중복을 막아주고
2. 문자열을 가지고 HTML을 만들어서 마크업 구조를 한눈에 보기 쉽게 코드를 작성했음!!

 ✨ innerHTML  : DOM API가 제공하는 속성. 
 문자열을 넣었는데 문자열안에 HTML태그 포함되어 있으면 그 자체를 DOM API로, 
 즉 HTML로 실제로 변환하는 것이 자동으로 처리된다는 뜻.!

=> HTML문자열로 만들면 마크업 구조가 굉장히 잘 보인다는 것을 명확하게 알 수 있음.

🚨 🚨 🚨
현재 까지 만든 화면의 문제점 : 목록 화면과 title이 같은 화면에 계속 보이고 있음!
+ title을 클릭할 때마다 같은 화면에 추가되는 버그까지..

=> 여러 개의 화면을 갖고 있는 형태의 UI를 구성한다면, 화면에 보여지는 건 사용자가 보고있는 현재의 화면이고! 이전의 화면은 사라져야함!!

문제를 개선해보자 ~~~ 😎 ! !

4) 화면 처리기 만들기 _ Router : 01 구조 구축


const container = document.getElementById('root');

const ajax = new XMLHttpRequest(); 

const content = document.createElement('div');
const NEWS_URL = 'https://api.hnpwa.com/v0/news/1.json';
const CONTENT_URL='https://api.hnpwa.com/v0/item/@id.json'; 


function getData(url){
    
    // 데이터 가져오기
    ajax.open('GET', url, false);
    ajax.send();

    return JSON.parse(ajax.response);
}

const newFeed = getData(NEWS_URL);


const ul = document.createElement('ul');

window.addEventListener('hashchange',function(){
    const id =location.hash.substr(1); 
  
    const newsContent = getData(CONTENT_URL.replace('@id',id));
    const title = document.createElement('h1');

    container.innerHTML=`
    <h1>${newsContent.title}</h1>
    
    <div>
    <a href="#">목록으로</a>
    </div>
    `;
});

// 배열 사용해서 만들고자 하는 문자열들을 배열 안에 요소로 차곡차곡 쌓아 놓고 마지막에 합쳐서 하나의 문자열로 만드는 방식 (흔히 사용되는 방법)
const newsList=[];

newsList.push('<ul>'); // 위치를 기억하는건 매우 어렵기에 배열 요소에 가장 마지막에 새로 추가하는 push 메소드 사용.

// for문 안에는 li태그 문자열 넣기
// 이 반복문은 li태그 문자열을 만들어서 newsList배열에 추가하는 역할. (자연스럽게 DOM API소멸)
for (let i = 0; i < newsFeed.length; i++) {
    
    newsList.push(`
        <li>
            <a href="#${newsFeed[i].id}">
            ${newsFeed[i].title} (${newsFeed[i].comments_count})
            </a>
        </li>
    `); // createElement로 만들었던 div 필요없어짐. 

}

newsList.push('/<ul>'); // ul태그 닫기

// 오버라이트하기 위해 innerHTML사용. 
// newsList는 배열이라 innerHTML에 넣을 수 없음..innerHTML에는 하나의 문자열이 들어가야 함. 
// newsList는 배열을 여러개 갖고 있고, 내용 자체는 태그들. -> 이것들을 하나의 문자열로 합치기.
container.innerHTML = newsList.join(''); //  join() : 하나의 문자열로 합치는 함수. 구분자 제거위해 빈 문자열 넣어주기.

1. 내용 화면으로 진입 = hashchange의 이벤트 핸들러.
=> hashchange의 이벤트 핸들러를 바꿈!
2. title 클릭하면 화면이 전환되면서 글 내용 화면으로 진입되는 것 확인 가능.
3. '목록으로' 클릭 했을 때 아직 글 목록으로 가지 못함..!!

4) 화면 처리기 만들기 _ Router : 02 함수 분리


const container = document.getElementById('root');

// 서버 (네트워크 너머에 있는 데이터) 가져오는 도구 = ajax 
const ajax = new XMLHttpRequest(); // new XMLHttpRequest()가 반환하는 값을 저장하는 저장소 만들어주기.
// ajax라는 변수 통해서 XMLHttpRequest가 제공하는 도구를 사용할 수 있음.

const content = document.createElement('div');
const NEWS_URL = 'https://api.hnpwa.com/v0/news/1.json'; // 바뀔 가능성이 있는 데이터는 따로 변수로 빼주는 것이 좋음. 
const CONTENT_URL='https://api.hnpwa.com/v0/item/@id.json'; // id값 마킹하기 위해 @사용(방법 여러가지 있음.)


function getData(url){
    
    // 데이터 가져오기
    ajax.open('GET', url, false); 
    ajax.send(); 

    return JSON.parse(ajax.response); 
}


function newsFeed(){
const newsFeed = getData(NEWS_URL);
const newsList=[];

newsList.push('<ul>'); 

for (let i = 0; i < newsFeed.length; i++) {    
    newsList.push(`
        <li>
            <a href="#${newsFeed[i].id}">
            ${newsFeed[i].title} (${newsFeed[i].comments_count})
            </a>
        </li>
    `); 
}

newsList.push('</ul>'); 


container.innerHTML = newsList.join(''); 
}

function newsDetail(){
    const id =location.hash.substr(1); 

    const newsContent = getData(CONTENT_URL.replace('@id',id));
    const title = document.createElement('h1');

    container.innerHTML=`
    <h1>${newsContent.title}</h1>
    
    <div>
    <a href="#">목록으로</a>
    </div>
    `;
}

// 라우터 생성 - 화면 전환 구현 (해시 값 갖고 오기)
// 페이지 시작하자 마자 게시글 보여주기 => newsFeed 함수 호출
// router함수는 newsDetail함수와 달리 화면을 전환하는 목적이 있기 때문에, 특정 화면의 어떠한 데이터에 대해서는 관심x
function router(){
    // 해시값 전체 가져오기 
    const routePath = location.hash;

    if(routePath ===''){
        newsFeed();
    }else{
        newsDetail();
    }

}


// 해시체인지에 router연결
window.addEventListener('hashchange', router); 
// 해시체인지가 일어날 때 동작하는 함수를 기존의 newsDetail이 아니라 router에게 주면 라우터가 해시 바뀔때 마다 동작하게 됨.
// 라우터 안에서 어떤 해시냐에 따라 글 목록 보여줄 때도 있고, 내용 보여줄 때도 있고.. 이렇게 동작하게 됨.


// 라우터 호출
router();

// 라우터 -> 화면 전환되어야 할 때 라우터가 판단해서 해당 화면으로 전환시킴.
// 현재 화면이 전환되어야 할 때는 해시체인지가 일어날 때 (해시가 바뀌는 걸 화면 전환을 위한 트리거로써 사용중.)

1. 글 목록 화면을 함수로 묶어줌! => newsDetail

✨ Router : 일종의 중계기 같은 것. 화면을 중계. 화면 전환    
화면이 여러개 있다면 어떤 상태에서는 A화면, 어떤 상태에서는 B화면, 어떤 상태에서는 C화면.....을
보여줄 수 있는 것!

2. 페이지 시작 시, 글 목록이 잘 보이고 글 목록 (title)을 클릭해서 내용을 보면 내용도 잘 표시 됨!
'목록으로' 를 클릭하면 글 목록으로 화면이 잘 전환됨.

3. 라우터 구현으로 화면 전환 자연스럽게 잘 일어남 ~~!

📍 알고 넘어가기!

 if(routePath ===''){
        newsFeed();
    }

-> 글 목록의 링크는 #이다.
그러면 location.hash#이 들어왔을 텐데 왜 이게 true일까? 라고한다면!
실제로 location.hash#만 들어 있을 경우에는 빈값을 반환함! 그래서 이 코드가 작동하 는 것~~!
그래서 라우터만 구현했을 뿐이지만, 글 목록도 잘 작동하는 것을 확인할 수 있음 😋

바로 다음 게시물은 본격적인 기능 추가 ! ~ ~ ~ !

profile
Front-end developer 👩‍💻✍

0개의 댓글