2022년 9월 작업물입니다.
부트캠프 과제로 '나만의 질의응답 사이트 만들기'를 진행했다.
과제를 진행했던 과정과 어려웠던 점, 그리고 그걸 해결해나간 과정을 백업해보고자 한다.

배포 링크 : https://fmapark.github.io/fe-sprint-my-agora-states/
부트캠프에서 제공한 가이드를 따라 아고라 스테이츠(부트캠프의 질의응답 사이트)의 구조와 부트캠프에서 제공해준 코드를 참고하여 전체적인 웹페이지의 구상도를 그려보았다.

전체적으로는 질문을 입력하는 부분과 입력된 질문을 출력하는 부분의 두 섹션으로 나누어 구상하였다.
질문을 입력받는 부분은 모두 줄바꿈이 일어나므로 block 엘리먼트 중 하나인 <div>를 활용하고, 출력 부분은 질문의 개수에 따라 유동적으로 늘어나는 형태이므로 <ul> <li>를 활용하기로 결정했다.
각 섹션을 감싸는 클래스 이름도 미리 지정해두었으나, 이 부분은 막상 html으로 구현해보니 조금 바뀌었다. (head와 body 부분을 나누어주어야 하고, 섹션 부분의 태그를 따로 지정해주어야 한다는 사실을 간과했다.)
위의 구상도를 바탕으로 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를 아무것도 입히지 않았으므로 아래와 같은 형태가 나왔다.

너무너무 못생긴 모습에 css를 먼저 입힐까 잠깐 고민했지만 꾹 참고 기능부터 구현했다.
부트캠프 측에서 함수명과 기본 구조, 그리고 답변 데이터셋은 제공해주어, 이를 이해하는 것을 우선으로 한 뒤 내가 추가해서 구현할 부분이 무엇인지 생각해보았다.
일단 최소한의 질의응답 사이트 구현을 위해 필요한 기능은 이름과 제목, 질문을 입력했을 때 해당 질의응답을 화면에 출력하는 것이라고 생각했다.
이를 위해 추가해야 할 부분은 다음과 같았다.
이를 차례로 구현해보았다.
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를 활용한 한국에 맞는 날짜 표기는 생각지 못한 부분이었지만 실시간 세션을 통해 추가할 수 있었다. 덕분에 가독성이 조금 더 깔끔해졌다.
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과 답변까지 표시해주려면 추가적인 페이지 구현이 더 있어야 할 것 같다.
이벤트리스너 함수 안에 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);

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/