[Web] 나만의 질의응답 사이트 만들기

FMA·2024년 3월 30일

HTML/CSS/JS

목록 보기
7/9

2022년 9월 작업물입니다.

부트캠프 과제로 '나만의 질의응답 사이트 만들기'를 진행했다.

과제를 진행했던 과정과 어려웠던 점, 그리고 그걸 해결해나간 과정을 백업해보고자 한다.

배포 링크 : https://fmapark.github.io/fe-sprint-my-agora-states/

1. Mock-up

1-1. 전체적인 구조 짜기

부트캠프에서 제공한 가이드를 따라 아고라 스테이츠(부트캠프의 질의응답 사이트)의 구조와 부트캠프에서 제공해준 코드를 참고하여 전체적인 웹페이지의 구상도를 그려보았다.

전체적으로는 질문을 입력하는 부분과 입력된 질문을 출력하는 부분의 두 섹션으로 나누어 구상하였다.

질문을 입력받는 부분은 모두 줄바꿈이 일어나므로 block 엘리먼트 중 하나인 <div>를 활용하고, 출력 부분은 질문의 개수에 따라 유동적으로 늘어나는 형태이므로 <ul> <li>를 활용하기로 결정했다.

각 섹션을 감싸는 클래스 이름도 미리 지정해두었으나, 이 부분은 막상 html으로 구현해보니 조금 바뀌었다. (head와 body 부분을 나누어주어야 하고, 섹션 부분의 태그를 따로 지정해주어야 한다는 사실을 간과했다.)

1-2. 컴포넌트 구성하기

위의 구상도를 바탕으로 html을 작성했다.

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My Agora States</title>
</head>

<body>
  <main>
    <h1>My Agora States</h1>
    <section class="form__container">
      <form action="" method="get" class="form">
        <div class="form__input--wrapper">
          <div class="form__input--name">
            <label for="name">이름: </label>
            <input type="text" name="name" id="name" required>
          </div>
          <div class="form__input--title">
            <label for="name">제목: </label>
            <input type="text" name="name" id="name" required>
          </div>
          <div class="form__textbox">
            <label for="story">질문: </label>
            <textarea id="story" name="story" placeholder="질문을 작성하세요" required></textarea>
          </div>
        </div>
        <div class="form__submit">
          <input type="submit" value="제출">
        </div>
      </form>
    </section>
    <section class="discussion__wrapper">
      <ul class="discussions__container">
        <li class="discussion__container">
          <div class="discussion__avatar--wrapper">
            <img class="discussion__avatar--image"
              src="https://avatars.githubusercontent.com/u/12145019?s=64&u=5c97f25ee02d87898457e23c0e61b884241838e3&v=4"
              alt="avatar of kimploo">
          </div>
          <div class="discussion__content">
            <h2 class="discussion__title"><a href="https://github.com/codestates-seb/agora-states-fe/discussions/6">[notice] 좋은 질문하는 법</a></h2>
            <div class="discussion__information">kimploo / 2022-04-22T14:08:33Z</div>
          </div>
          <div class="discussion__answered"><p></p></div>
        </li>
      </ul>
    </section>
  </main>
</body>

</html>

아직 css를 아무것도 입히지 않았으므로 아래와 같은 형태가 나왔다.

2. 기능 구현하기

너무너무 못생긴 모습에 css를 먼저 입힐까 잠깐 고민했지만 꾹 참고 기능부터 구현했다.

부트캠프 측에서 함수명과 기본 구조, 그리고 답변 데이터셋은 제공해주어, 이를 이해하는 것을 우선으로 한 뒤 내가 추가해서 구현할 부분이 무엇인지 생각해보았다.

일단 최소한의 질의응답 사이트 구현을 위해 필요한 기능은 이름과 제목, 질문을 입력했을 때 해당 질의응답을 화면에 출력하는 것이라고 생각했다.

이를 위해 추가해야 할 부분은 다음과 같았다.

  • 데이터를 DOM형식으로 바꿔주기
  • 이벤트리스너 함수 작성 -> '제출' 버튼을 누르면 질의응답이 추가되도록 함
  • 추가된 질의응답을 데이터셋에도 추가

이를 차례로 구현해보았다.

2-1. 데이터를 DOM형식으로 바꿔주기

script.js

const convertToDiscussion = (obj) => { //매개변수로 객체 받기
  const li = document.createElement("li"); // li 요소 생성
  li.className = "discussion__container"; // 클래스 이름 지정

  //html element를 DOM형식으로 바꿀 수 있게 받아오기
  const avatarWrapper = document.createElement("div");
  avatarWrapper.className = "discussion__avatar--wrapper";
  const discussionContent = document.createElement("div");
  discussionContent.className = "discussion__content";
  const discussionAnswered = document.createElement("div");
  discussionAnswered.className = "discussion__answered";

  //받아온 element를 DOM에 넣어주기
  
  //1. 아바타 영역
  const avatarImg = document.createElement("img");
  avatarImg.src = obj.avatarUrl;
  avatarImg.alt = "avatar of " + obj.author;
  avatarWrapper.append(avatarImg);

  //2. 콘텐츠 영역
  //2-1 제목
  const contentTitle = document.createElement('h2');
  const titleAnchor = document.createElement('a');
  contentTitle.className = "discussion__title";
  titleAnchor.textContent = obj.title;
  titleAnchor.href = obj.url;

  contentTitle.append(titleAnchor);

  //2-2. 정보
  const contentInfo = document.createElement('div')
  contentInfo.textContent = `${obj.author} / ${new Date(obj.createdAt).toLocaleString()}` //날짜표기
  contentInfo.className = "discussion__information" 
  discussionContent.append(contentTitle, contentInfo);
 
  //3. 체크박스 영역
  const checked = document.createElement('p');
  discussionAnswered.append(checked);
  checked.textContent = obj.answer ? "☑" : "☒" //네모안에 엑스 있는 특수문자

  li.append(avatarWrapper, discussionContent, discussionAnswered);
  return li;
};

아직 DOM 구조도 익숙지 않고 다른 사람의 코드를 기반으로 작성하는 것도 익숙지 않아 이곳에서 애를 많이 먹었다.

몇 번의 시도 끝에 'element 받아오기 - DOM 구조로 변수 설정 - 변수를 통해 조작' 이 세 단계를 차례로 거치면 조금 더 수월하다는 것을 알아내었고, 그 이후로는 큰 어려움 없이 진행할 수 있었다.

Date를 활용한 한국에 맞는 날짜 표기는 생각지 못한 부분이었지만 실시간 세션을 통해 추가할 수 있었다. 덕분에 가독성이 조금 더 깔끔해졌다.

2-2. 이벤트리스너 함수 작성

script.js (일부 데이터가 삭제된 코드입니다.)

//이벤트 리스너
const form = document.querySelector('.form');
const author = document.querySelector('.form__input--name > input');
const title = document.querySelector('.form__input--title > input');
const textArea = document.querySelector('.form__textbox > textarea')

form.addEventListener('submit', (event) => {
  event.preventDefault();
  // 객체를 하나 만든다
  const obj = {
    id: "unique number",
    createdAt: new Date(), //앞에서 만들였던 날짜객체 불러오기 - > 지금시간
    title: title.value,
    url: "",
    author: author.value,
    answer: {
      id: "",
      createdAt: "2022-05-16T02:09:52Z",
      url: "",
      author: "Dyract",
      bodyHTML:
        ''
        avatarUrl: "",
    },
    bodyHTML:
      ''
      avatarUrl:
      "",
  }

  //제출 시 초기화
  title.value = "";
  author.value = "";
  textArea.value = "";

})

질의응답 데이터는 data.js라는 파일에 따로 저장되어 있다.

데이터셋 전체를 새로 설정하기에는 무리가 있어 기존에 제공되어 있는 데이터셋 객체 중 하나를 그대로 불러온 뒤, 입력값인 '이름', '제목', '질문', 그리고 작성 시점을 나타내는 날짜만 수정하여 추가하는 방식으로 구현하였다.

질의응답 url과 답변까지 표시해주려면 추가적인 페이지 구현이 더 있어야 할 것 같다.

2-3. 데이터셋에 질의응답 추가

이벤트리스너 함수 안에 data.js의 객체에 unshift 해주는 부분만 추가해주었다.

위의 코드가 아래와 같이 변경되었다.

script.js (일부 데이터가 삭제된 코드입니다.)

//이벤트 리스너
const form = document.querySelector('.form');
const author = document.querySelector('.form__input--name > input');
const title = document.querySelector('.form__input--title > input');
const textArea = document.querySelector('.form__textbox > textarea')

form.addEventListener('submit', (event) => {
  event.preventDefault();
  // 객체를 하나 만든다
  const obj = {
    id: "unique number",
    createdAt: new Date(), //앞에서 만들였던 날짜객체 불러오기 - > 지금시간
    title: title.value,
    url: "",
    author: author.value,
    answer: {
      id: "",
      createdAt: "2022-05-16T02:09:52Z",
      url: "",
      author: "Dyract",
      bodyHTML:
        ''
        avatarUrl: "",
    },
    bodyHTML:
      ''
      avatarUrl:
      "",
  }
  //더미데이터에 작성한 데이터 추가
  agoraStatesDiscussions.unshift(obj);
  // 그 객체를 convertToDiscussion에 넣어서 DOM으로 변환
  // 그걸 또 render함수에 넣어서 브라우저에 렌더링 ->  맨앞으로
  ul.prepend(convertToDiscussion(obj));

  //제출 시 초기화
  title.value = "";
  author.value = "";
  textArea.value = "";

})

데이터셋을 화면에 렌더링하는 부분은 제공받은 코드에도 작성되어 있어 따로 건드리지 않았다.

script.js

// agoraStatesDiscussions 배열의 모든 데이터를 화면에 렌더링하는 함수입니다.
const render = (element) => {
  // 더미데이터의 길이만큼, 더미데이터 안에 있는 모든 요소를 탐색
  for (let i = 0; i < agoraStatesDiscussions.length; i += 1) {
    // i번째 요소를 convertToDiscussion에 전달해서 ul에 append
    element.append(convertToDiscussion(agoraStatesDiscussions[i]));
  }
  return;
};

// ul 요소에 agoraStatesDiscussions 배열의 모든 데이터를 화면에 렌더링합니다.
const ul = document.querySelector("ul.discussions__container");
render(ul);

3. CSS 작성

CSS를 덧씌워 처음 사진과 같은 디자인을 완성시켰다.

요즘 다크모드에 꽂혔기 때문에 검흰 베이스로 파스텔톤을 더하고, margin, padding 조정과 줄 추가를 통해 가독성을 높였다.

style.css

@import url('https://fonts.googleapis.com/css2?family=Do+Hyeon&family=Jua&display=swap');

* {
    margin : 0;
    padding : 0;
    box-sizing: border-box;
    font-family: 'Jua', sans-serif;
}

body {
    margin : 0;
    padding : 0;
    background-color: black;
    display: flex;
    justify-content: center;
    align-items: center;
}

main {
    width : 540px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

li {
    list-style: none;
}

h1 {
    color : white;
    font-family: Georgia, serif;
    margin-top: 1.5rem;
}

.form__input--wrapper input {
    width : 200px;
}

.form__input--wrapper textarea {
    width : 200px;
}

.form__container {
    margin-top: 2rem;
    margin-bottom: 2rem;
}

.form__input--wrapper > div {
    padding: 0.6rem;
}

.form__submit {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 0.5rem;
}

.form__submit input {
    padding: 0.3rem 1.5rem;
    font-size: 17px;
    border-radius: 10px;
}

.form label {
    color : white;
    vertical-align : top;
    margin-right: 0.5rem;
}

.discussion__container {
    display: flex;
    width: 540px;
    justify-content: space-between;
    border-bottom: 1px solid white;
    margin-top: 1.5rem;
    padding-bottom: 1.5rem;
}

.discussion__avatar--wrapper > img {
    border-radius: 50%;
    width : 48px
}

.discussion__avatar--wrapper {
    display:flex;
    justify-content: center;
    align-items: center;
    margin-right: 1rem;
}

.discussion__content {
    flex-grow: 1;
    display: flex;
    flex-direction: column;
    justify-content: space-around;
}

.discussion__title{
    margin-bottom: 0.5rem;
}

.discussion__title > a {
    text-decoration: none;  
    font-size: 22px;
    color : #9b92ff;
}

.discussion__information {
    text-align: right;
    color: white;
}

.discussion__answered {
    display: flex;
    justify-content: center;
    align-self: center;
    font-size: xx-large;
    margin-left: 1.2rem;
    color : white;
}

깃허브 배포

전체 코드는 아래 github 레포지토리에서 확인할 수 있다.

https://github.com/fmaPark/fe-sprint-my-agora-states

배포 링크 : https://fmapark.github.io/fe-sprint-my-agora-states/

결론 및 피드백

배운 점

  • 비교적 이해가 미흡했던 DOM구조를 직접 구현해보며 감을 잡을 수 있었다.
  • 이전 과제에서는 css 속성을 어떤 걸 사용해야 할지 전혀 감을 잡지 못했지만, 지금은 머릿속에 있는 디자인을 어느정도 코드로 끌어낼 수 있게 되었다.

아쉬웠던 점

  • html은 구조도를 통해 개요를 짠 상태로 개발해 수월하게 코드를 작성했던 반면, css는 내가 어떤 웹페이지를 만들고자 하는지 모르는 상태로 무작정 코드를 두들겨 완성에 어려움이 있었다.어휘의 부족이라고 생각한다. css를 통해 구현할 수 있는 부분에 어떤 것이 있는지 잘 모르는 상태로 개발을 시작했기 때문에 머릿속에 그려보는 데도 한계가 있었을 것이다. 예제를 통해 디자인적 요소의 공부가 더 필요할 것 같다.머릿속에만 웹사이트를 그려보았기 때문에 구체화가 힘들었다고 생각한다. html에 구조도를 그렸듯 눈에 보이는 디자인 구상을 해보아야겠다고 생각했다. (일종의 프로토타입의 필요성을 느낀 게 아닐까?)
  • 시간 부족으로 단순 내용 업데이트 이외의 기능 구현을 생각해보지 못했다. 이는 그대로 완성도에 영향을 주었다.이건 내 일정 조정 미스였다... 이번 프로젝트는 주말 휴식을 반납해서 어떻게든 따라가보겠지만, 앞으로 부트캠프 프로젝트 기간에는 웬만하면 다른 일을 병행하지 말자.
  • 깃 사용이 미숙했던 탓에 중간에 브런치가 꼬여버렸고, 발표시간이 다가오자 급한 마음에 커밋 기록을 싹 삭제해버리고 새로 push했다. 그래서 커밋 기록이 싹 날아갔다...커밋 메시지 작성 연습은 수정 시에 다시 하는걸로 해야겠다.리셋 대신 커밋 기록삭제 기능을 적극적으로 이용했다면 어땠을까... 하는 아쉬움이 있다.

앞으로 개선할 점 (추가하고자 하는 기능)

  • 질문 삭제 기능
  • 질문 검색 기능
  • css 호버 효과 적용
  • 페이지네이션 기능
  • 디스커션 유지 기능

0개의 댓글