@@ -1,83 +1,112 @@
 import GithubApiController from "@controllers/githubController";
 import User from "@models/User";
+import {
+  NO_SEARCH_RESULT_TEMPLATE,
+  SEARCH_LOADING_TEMPLATE,
+  getReposTemplate,
+  NO_REPOS_TEMPLATE,
+} from "@templates/search";
+import { SPINNER_TEMPLATE } from "@templates/spinner";
+import { NUMBER_OF_REPOS } from "@constants/search";
 
 const searchResultContainer = document.body.querySelector(".search-result");
 
 export default class SearchController {
-  constructor(inputEl, submitEl) {
+  constructor(inputEl, submitEl, historyController) {
     this.inputEl = inputEl;
     this.submitEl = submitEl;
     this.fetcher = new GithubApiController();
+    this.historyController = historyController;
     this.init();
   }
   init() {
     this.inputEl.addEventListener("keypress", (event) => {
-      if (event.code === 'Enter') {
+      if (event.code === "Enter") {
         this.search(this.inputEl.value);
       }
     });
     this.submitEl.addEventListener("click", () => {
       this.search(this.inputEl.value);
     });
+
+    this.historyController.historyContainer.addEventListener(
+      "click",
+      (event) => {
+        if (
+          event.target.classList.contains("list-group-item") &&
+          !event.target.classList.contains("button__delete")
+        ) {
+          const [searchValue] = event.target.textContent.trim().split("\n");
+          this.search(searchValue);
+        }
+      }
+    );
   }
 
   async search(searchValue) {
     const renderUser = (user) => {
       const userResult = searchResultContainer.querySelector("#user-result");
       userResult.id = user.id;
-      userResult.innerHTML = user.render();
+      user.render(userResult);
     };
-
-    const renderRepos = (repos) => {
+    const renderNoUserInfo = () => {
+      const userResult = searchResultContainer.querySelector("#user-result");
+      userResult.innerHTML = NO_SEARCH_RESULT_TEMPLATE;
+    };
+    const createRepoResultContainerEl = () => {
       const repoResult = document.createElement("div");
-      repoResult.id = "#repos-result";
+      repoResult.id = "repo-result";
       repoResult.className = "bs-component";
+      return repoResult;
+    };
+    const renderRepos = (repos, numberOfRepos) => {
+      const repoResult = createRepoResultContainerEl();
+      repoResult.innerHTML = getReposTemplate(repos, numberOfRepos);
 
-      repoResult.innerHTML = `
-            <div class="container">
-              <div class="row">
-                ${repos
-                  .slice(0, 5)
-                  .map((repo) => repo.render())
-                  .join("\n")}
-              </div>
-            </div>
-          `;
       searchResultContainer.appendChild(repoResult);
     };
+    const renderNoRepos = () => {
+      const repoResult = createRepoResultContainerEl();
+      repoResult.innerHTML = NO_REPOS_TEMPLATE;
+
+      searchResultContainer.appendChild(repoResult);
+    };
+    const createLoadingElement = () => {
+      const element = document.createElement("div");
+      element.className = "card-body d-flex align-items-center";
+      element.innerHTML = SPINNER_TEMPLATE;
+      return element;
+    };
 
     const trimmedValue = searchValue.trim();
     if (!trimmedValue) {
       alert("검색어를 입력해주세요");
       return;
     }
-
-    searchResultContainer.innerHTML = `
-      <div class="card my-3">
-        <h4 class="card-header">검색결과</h4>
-        <div id="user-result" class="card-body d-flex align-items-center">
-          <p class="text-center container-fluid">
-            검색중
-          </p>
-        </div>
-      </div>
-    `;
+    this.historyController.addHistory(trimmedValue);
+    searchResultContainer.innerHTML = SEARCH_LOADING_TEMPLATE;
 
     const userInfo = await this.fetcher.getUser(trimmedValue);
     if (!userInfo) {
-      const userResult = searchResultContainer.querySelector("#user-result");
-      userResult.innerHTML = `
-        <p class="text-center container-fluid">
-          검색 결과가 없습니다.
-        </p>
-      `;
+      renderNoUserInfo();
       return;
     }
 
     const user = new User(userInfo);
     renderUser(user);
 
-    user.setRepos(await this.fetcher.getRepos(user));
-    renderRepos(user.repos);
+    const loadingEl = createLoadingElement();
+
+    searchResultContainer.appendChild(loadingEl);
+    const repos = await this.fetcher.getRepos(user);
+    searchResultContainer.removeChild(loadingEl);
+
+    if (repos.length === 0) {
+      renderNoRepos();
+      return;
+    }
+
+    user.setRepos(repos);
+    renderRepos(user.repos, NUMBER_OF_REPOS);
   }
 }

리뷰: 개인적인 생각으론 함수명이 `getXXXEl`로 되어있어서 기존에 존재하는 엘레먼트를 쿼리셀렉트로 가져오는 것 같은 느낌이 있는데요, 새로운 엘레먼트를 만들어서 반환하는 역할을 해주고 있으니 `get` 대신 `create`라는 이름으로 네이밍해도 좋을 것 같네요 :)