diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..54d714d
Binary files /dev/null and b/.DS_Store differ
diff --git a/README.md b/README.md
index fbbc6dd..c321945 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,22 @@
# 프로그래머스 해설 사이트
+
## ✋ 개발 전 필수 확인!
+
1. `npm install`을 통해 개발 종속 패키지들 설치
2. PR 시 project, milestone, reviewer, assignee 설정 필수
3. PR 시 변경 사항 설명 필수
-
## 📐 커밋 컨벤션
+
### 커밋 예시
+
- `feat: 검색 시 초성 검색이 가능하도록 구현`
- `refactor: 검색 결과 배열 반환 함수를 utils 폴더로 분리`
-| 커밋 헤더 | 설명 | 추가 설명 |
-|----------| --------------------- | ------------------------------------------------------------ |
-| feat | 기능추가/수정/삭제 | 제품 코드 수정 발생 |
-| refactor | 리팩토링 | 제품 코드 수정 발생 |
-| fix | 버그 수정 | 제품 코드 수정 발생 |
-| docs | 문서 추가, 수정, 삭제 | 코드 수정 없음 |
-| etc | 그 외 모든 수정 | 유형이 혼재된 경우, 되도록이면 커밋 분리. 분리가 어려울 경우 위 순서 상 상위 항목의 유형으로 작성 |
-
+| 커밋 헤더 | 설명 | 추가 설명 |
+| --------- | --------------------- | ------------------------------------------------------------------------------------------------- |
+| feat | 기능추가/수정/삭제 | 제품 코드 수정 발생 |
+| refactor | 리팩토링 | 제품 코드 수정 발생 |
+| fix | 버그 수정 | 제품 코드 수정 발생 |
+| docs | 문서 추가, 수정, 삭제 | 코드 수정 없음 |
+| etc | 그 외 모든 수정 | 유형이 혼재된 경우, 되도록이면 커밋 분리. 분리가 어려울 경우 위 순서 상 상위 항목의 유형으로 작성 |
diff --git a/Section/Footer/footer.js b/Section/Footer/footer.js
deleted file mode 100644
index ef4d0db..0000000
--- a/Section/Footer/footer.js
+++ /dev/null
@@ -1,36 +0,0 @@
-export default function Footer() {
- const footer = document.querySelector("#footerBox");
-
- let template = `
-
-
- `;
-
- template = template.replaceAll("{{__레포지토리주소__}}", "https://github.com/codeisneverodd/programmers-coding-test")
- footer.innerHTML = template;
-
- }
\ No newline at end of file
diff --git a/Section/SearchBox/components/SearchInput.js b/Section/SearchBox/components/SearchInput.js
deleted file mode 100644
index 1f205ee..0000000
--- a/Section/SearchBox/components/SearchInput.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import { CreateFuzzyMatcher } from '../utils/koreanFuzzy.js';
-
-export function SearchInput() {
- this.render = () => {
- const $searchInput = document.querySelector('.searchInput');
- $searchInput.innerHTML = `
-
- `;
- const $fileListItem = document.querySelectorAll('.file-list-item');
-
- $searchInput.addEventListener('input', event => {
- const $searchInputBox = document.getElementById('searchInput');
- const query = $searchInputBox.value;
- const regex = CreateFuzzyMatcher(query.toLowerCase());
- for (let i = 0; i < $fileListItem.length; i++) {
- if (regex.test($fileListItem[i].textContent.toLowerCase())) {
- $fileListItem[i].classList.remove('is-hidden');
- } else {
- $fileListItem[i].classList.add('is-hidden');
- }
- }
- });
- };
-}
diff --git a/Section/SearchBox/components/SearchableList.js b/Section/SearchBox/components/SearchableList.js
deleted file mode 100644
index 43898eb..0000000
--- a/Section/SearchBox/components/SearchableList.js
+++ /dev/null
@@ -1,49 +0,0 @@
-//TODO: api 함수 및 상수 분리
-import SearchResult from '../../SearchResult/components/index.js';
-import { getFileList } from '../utils/api.js';
-
-const POSSIBLE_LEVELS = [1, 2, 3, 4, 5];
-export function SearchableList() {
- const $searchableList = document.querySelector('.searchableList');
- this.render = async () => {
- $searchableList.innerHTML = `
-
- `;
- const $fileListContainer = document.querySelector('.file-list-container');
- await fillList();
- async function fillList() {
- const fileList = {};
- for (const level of POSSIBLE_LEVELS) {
- fileList[level] = await getFileList(level);
- delete fileList[level][0];
- }
- $fileListContainer.innerHTML = `
- ${POSSIBLE_LEVELS.map(
- level => `
-
- [level ${level}]
- ${fileList[level]
- .map(
- file =>
- `${file.name
- .slice(0, file.name.length - 3)
- .replaceAll('-', ' ')} `,
- )
- .join('')}
- `,
- ).join('')}
- `;
- }
-
- $fileListContainer.addEventListener('click', e => {
- if (e.target.tagName !== 'LI') return;
- const level = e.target.parentNode.classList[1].slice(-1);
- const fileName = e.target.classList[1];
- const $searchResult = new SearchResult({
- level,
- fileName,
- });
- $searchResult.render();
- });
- };
-}
diff --git a/Section/SearchBox/components/index.js b/Section/SearchBox/components/index.js
deleted file mode 100644
index d46ae2c..0000000
--- a/Section/SearchBox/components/index.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { SearchInput } from './SearchInput.js';
-import { SearchableList } from './SearchableList.js';
-
-export default function SearchBox() {
- const $searchBox = document.querySelector('.searchBox');
- this.render = async () => {
- $searchBox.innerHTML = `
-
-
-
- `;
- const searchInput = new SearchInput();
- const searchableList = new SearchableList();
-
- await searchableList.render();
- searchInput.render();
- };
-}
diff --git a/Section/SearchBox/utils/api.js b/Section/SearchBox/utils/api.js
deleted file mode 100644
index ae7e4c6..0000000
--- a/Section/SearchBox/utils/api.js
+++ /dev/null
@@ -1,6 +0,0 @@
-const GET_FILE_LIST_BASE_URL = `https://api.github.com/repos/codeisneverodd/programmers-coding-test/contents/`;
-export const getFileList = async level => {
- const url = GET_FILE_LIST_BASE_URL + `level-${level}`;
- const response = await fetch(url);
- return await response.json();
-};
diff --git a/Section/SearchBox/utils/koreanFuzzy.js b/Section/SearchBox/utils/koreanFuzzy.js
deleted file mode 100644
index 8a65ceb..0000000
--- a/Section/SearchBox/utils/koreanFuzzy.js
+++ /dev/null
@@ -1,38 +0,0 @@
-const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
-
-const ch2pattern = ch => {
- const offset = 44032;
- if (/[가-힣]/.test(ch)) {
- const chCode = ch.charCodeAt(0) - offset;
- if (chCode % 28 > 0) {
- return ch;
- }
- const begin = Math.floor(chCode / 28) * 28 + offset;
- const end = begin + 27;
- return `[\\u${begin.toString(16)}-\\u${end.toString(16)}]`;
- }
- if (/[ㄱ-ㅎ]/.test(ch)) {
- const con2syl = {
- ㄱ: '가'.charCodeAt(0),
- ㄲ: '까'.charCodeAt(0),
- ㄴ: '나'.charCodeAt(0),
- ㄷ: '다'.charCodeAt(0),
- ㄸ: '따'.charCodeAt(0),
- ㄹ: '라'.charCodeAt(0),
- ㅁ: '마'.charCodeAt(0),
- ㅂ: '바'.charCodeAt(0),
- ㅃ: '빠'.charCodeAt(0),
- ㅅ: '사'.charCodeAt(0),
- };
- const begin =
- con2syl[ch] || (ch.charCodeAt(0) - 12613) * 588 + con2syl['ㅅ'];
- const end = begin + 587;
- return `[${ch}\\u${begin.toString(16)}-\\u${end.toString(16)}]`;
- }
- return escapeRegExp(ch);
-};
-
-export const CreateFuzzyMatcher = input => {
- const pattern = input.split('').map(ch2pattern).join('.*?');
- return new RegExp(pattern);
-};
diff --git a/Section/SearchResult/components/index.js b/Section/SearchResult/components/index.js
deleted file mode 100644
index 9201722..0000000
--- a/Section/SearchResult/components/index.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import { getFileContent } from "../utils/api.js";
-import { formattedFileName, splitCodeToSolutions } from "../utils/format.js";
-import { copyText } from "../utils/copyText.js";
-
-export default function SearchResult({ level, fileName }) {
- let page = 0;
- this.render = async () => {
- const $searchResult = document.querySelector('.searchResult');
- $searchResult.innerHTML = `
-
- 이전 해설
- 다음 해설
-
-
-
- `;
- const $fileTitle = document.querySelector('.file-title');
- const $code = document.querySelector('.code');
- const solutions = splitCodeToSolutions(
- await getFileContent(level, fileName),
- );
- setCurrentResult();
- addCopyEvent();
- addNavEvent();
-
- function setCurrentResult() {
- $fileTitle.innerHTML = formattedFileName(fileName);
- $code.innerHTML = solutions[page];
- if(solutions.length > 1){
- const $btnNextSolution = document.querySelector('.btnNextSolution-inactive')
- $btnNextSolution.className = 'btnNextSolution'
- }
- }
-
- function addCopyEvent() {
- const $copyBtn = document.querySelector('.btn-copy');
- $copyBtn.addEventListener('click', e => {
- const src = e.target.previousElementSibling;
- copyText(src);
- const isCopied = document.querySelector('.isCopied');
- isCopied.textContent = ' 📋 클립보드에 복사됨!';
- setTimeout(() => {
- isCopied.textContent = '';
- }, 1000);
- });
- }
-
- function addNavEvent() {
- const $navigator = document.querySelector('.solutionNavigator');
- const btnPrevSolution = document.querySelector(
- '.btnPrevSolution-inactive',
- );
- const btnNextSolution = document.querySelector('.btnNextSolution');
- if (page === 0) {
- btnPrevSolution.className = 'btnPrevSolution-inactive';
- }
- $navigator.addEventListener('click', e => {
- const $clickedButton = e.target.closest('button');
-
- if ($clickedButton.className.includes('btnPrevSolution')) {
- if (page > 0) {
- page -= 1;
- $code.innerHTML = solutions[page];
- }
- }
- if ($clickedButton.className.includes('btnNextSolution')) {
- if (page < solutions.length - 1) {
- page += 1;
- $code.innerHTML = solutions[page];
- }
- }
- page !== 0
- ? (btnPrevSolution.className = 'btnPrevSolution')
- : (btnPrevSolution.className = 'btnPrevSolution-inactive');
- page === solutions.length - 1
- ? (btnNextSolution.className = 'btnNextSolution-inactive')
- : (btnNextSolution.className = 'btnNextSolution');
- });
- }
- };
-}
diff --git a/Section/SearchResult/utils/api.js b/Section/SearchResult/utils/api.js
deleted file mode 100644
index e2cdee0..0000000
--- a/Section/SearchResult/utils/api.js
+++ /dev/null
@@ -1,6 +0,0 @@
-const GET_FILE_CONTENT_BASE_URL = `https://raw.githubusercontent.com/codeisneverodd/programmers-coding-test/main/`;
-export const getFileContent = async (level, fileName) => {
- const url = GET_FILE_CONTENT_BASE_URL + `/level-${level}` + `/${fileName}`;
- const response = await fetch(url);
- return await response.text();
-};
diff --git a/Section/SearchResult/utils/copyText.js b/Section/SearchResult/utils/copyText.js
deleted file mode 100644
index 67a9637..0000000
--- a/Section/SearchResult/utils/copyText.js
+++ /dev/null
@@ -1,11 +0,0 @@
-export function copyText(src) {
- const t = document.createElement('textarea');
- document.body.appendChild(t);
- t.value = src.innerHTML
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/"/g, '"');
- t.select();
- document.execCommand('copy');
- document.body.removeChild(t);
-}
diff --git a/app.js b/app.js
deleted file mode 100644
index 1335594..0000000
--- a/app.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import SearchBox from './Section/SearchBox/components/index.js';
-import FooterBox from './Section/Footer/footer.js';
-
-const $app = document.querySelector('.app');
-const $loading = document.querySelector('.loading');
-
-const init = async () => {
- $app.innerHTML = `
-
-
-
- `;
- const $searchBox = new SearchBox();
- await $searchBox.render();
-};
-
-const loading = async () => {
- $loading.innerHTML = `
-
-
- Loading…
- `;
- await init();
- FooterBox();
- $loading.style.display = 'none';
-};
-
-loading();
\ No newline at end of file
diff --git a/index.html b/index.html
deleted file mode 100644
index 2b57603..0000000
--- a/index.html
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
- 프로그래머스 해설 은행
-
-
-
-
-
-
- 🔍 프로그래머스 해설 은행
-
-
-
-
-
diff --git a/public/index.html b/public/index.html
index 0f5f6bc..f3efa95 100644
--- a/public/index.html
+++ b/public/index.html
@@ -3,24 +3,19 @@
-
+
프로그래머스 해설 은행
-
+
diff --git a/src/.DS_Store b/src/.DS_Store
new file mode 100644
index 0000000..1ccffbb
Binary files /dev/null and b/src/.DS_Store differ
diff --git a/src/App.js b/src/App.js
index 43764b9..2681bb5 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,7 +1,16 @@
-function App() {
+import SearchBox from "./SearchBox/SearchBox";
+import SearchResult from "./SearchResult/SearchResult";
+import Footer from "./Footer/Footer";
+import Loading from "./Loading";
+
+export default function App() {
return (
-
+
+
🔍 프로그래머스 해설 은행
+
+
+
+
+
);
}
-
-export default App;
diff --git a/src/Footer/footer.js b/src/Footer/footer.js
index ef4d0db..e09633d 100644
--- a/src/Footer/footer.js
+++ b/src/Footer/footer.js
@@ -1,36 +1,63 @@
-export default function Footer() {
- const footer = document.querySelector("#footerBox");
+import gitHubLogoPath from "../images/github.png";
+import programmersLogoPath from "../images/programmers.png";
- let template = `
-