코딩테스트/프로그래머스
[프론트엔드/과제테스트] 고양이 사진첩 애플리케이션 기출 문제 해설 총 정리
배똥회장
2022. 8. 18. 09:56
728x90
'2021 Dev-Matching: 웹 프론트엔드 개발자(상반기)' 기출 문제 해설
'Dev-Matching 웹 프론트엔드 개발자'의 과제 테스트는 어떠셨나요? 내가 무엇을 잘못하였고, 무엇을 잘했는지 궁금하시지 않으셨나요? 우리 모두 해설을 보고 한번 점검하는 시간을 가지도록 해요.
prgms.tistory.com
위의 공식 문제 해설을 기반으로 작성한 코드입니다.
제대로 작동이 되지 않아 개인적으로 수정한 내용도 있으며, 추가한 내용도 있습니다.
▽ 깃허브로 편하게 보기 ▽
GitHub - b-sseung/21_DevMatching_1
Contribute to b-sseung/21_DevMatching_1 development by creating an account on GitHub.
github.com
📌 index.html
<!DOCTYPE html>
<html lang="ko">
<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>고양이 사진첩!</title>
<link rel="stylesheet" href="./src/styles/style.css">
<script src="./src/index.js" type="module"></script>
</head>
<body>
<h1>고양이 사진첩</h1>
<main class="app">
</main>
</body>
</html>
📌 index.js
import App from './App.js';
new App(document.querySelector('.app'));
📌 App.js
import ImageView from './ImageView.js';
import Breadcrumb from './Breadcrumb.js';
import Nodes from './Nodes.js';
import { request } from './api.js';
import Loading from './Loading.js';
const cache = {};
export default function App($app) {
this.state = {
isRoot: true,
nodes: [],
depth: [],
selectedFilePath: null,
isLoading: true
}
const breadcrumb = new Breadcrumb({
$app,
initialState: this.state.depth,
onClick: (index) => {
if (index === null) {
this.setState({
...this.state,
depth: [],
isRoot: true,
nodes: cache.root
})
} else {
if (index === this.state.depth.length - 1) return;
const nextState = {...this.state};
const nextDepth = this.state.depth.slice(0, index+1);
this.setState({
...nextState,
depth: nextDepth,
isRoot: false,
nodes: cache[nextDepth[nextDepth.length - 1].id]
})
}
}
})
const nodes = new Nodes({
$app,
initialState: {
isRoot: this.state.isRoot,
nodes: this.state.nodes
},
onClick: async (node) => {
try {
if (node.type === 'DIRECTORY') {
if (cache[node.id]) {
this.setState({
...this.state,
isRoot: false,
depth: [...this.state.depth, node],
nodes: cache[node.id]
});
} else {
const nextNodes = await request(node.id);
this.setState({
...this.state,
isRoot: false,
depth: [...this.state.depth, node],
nodes: nextNodes
});
cache[node.id] = nextNodes;
}
} else if (node.type === 'FILE') {
this.setState({
...this.state,
isRoot: false,
selectedFilePath: node.filePath
})
}
} catch (e) {
//에러처리하기
}
},
onBackClick: async() => {
try {
const nextState = {...this.state};
nextState.depth.pop();
const prevNodeId = nextState.depth.length === 0 ? null : nextState.depth[nextState.depth.length - 1].id;
if (prevNodeId === null) {
this.setState({
...nextState,
isRoot: true,
nodes: cache.root
})
} else {
this.setState({
...nextState,
isRoot: false,
nodes: cache[prevNodeId],
})
}
} catch (e) {
//에러 처리
}
}
})
const imageView = new ImageView({
$app,
initialState: this.state.selectedNodeImage,
onClick: async () => {
this.setState({
selectedFilePath: null
})
}
})
const loading = new Loading({
$app,
initialState: this.state.isLoading
});
this.setState = (nextState) => {
this.state = nextState;
breadcrumb.setState(this.state.depth);
nodes.setState({
isRoot: this.state.isRoot,
nodes: this.state.nodes
});
imageView.setState(this.state.selectedFilePath);
loading.setState(this.state.isLoading);
this.state.selectedFilePath = null;
}
const init = async () => {
try {
const rootNodes = await request();
this.setState({
...this.state,
isRoot: this.state.isRoot,
nodes: rootNodes,
isLoading: false
});
cache.root = rootNodes;
} catch (e) {
//에러처리 하기
}
}
init();
}
📌 Nodes.js
export default function Nodes({ $app, initialState, onClick, onBackClick }) {
this.state = initialState;
this.$target = document.createElement('ul');
this.$target.className = 'Nodes';
$app.appendChild(this.$target);
this.setState = (nextState) => {
this.state = nextState;
this.render();
}
this.onClick = onClick;
this.onBackClick = onBackClick;
this.render = () => {
if (this.state.nodes) {
const nodesTemplate = this.state.nodes.map(node => {
const iconPath = node.type === 'FILE' ? './assets/file.png' : './assets/directory.png';
return `
<div class="Node" data-node-id="${node.id}">
<img src="${iconPath}"/>
<div>${node.name}</div>
</div>
`
}).join('');
this.$target.innerHTML = this.state.isRoot ? nodesTemplate : `<div class="Node"><img src="./assets/prev.png"></div>${nodesTemplate}`;
}
};
this.$target.addEventListener('click', (e) => {
const $node = e.target.closest('.Node');
if ($node) {
const { nodeId } = $node.dataset;
if (!nodeId) this.onBackClick();
const selectedNode = this.state.nodes.find(node => node.id === nodeId)
if (selectedNode) this.onClick(selectedNode);
}
})
this.render();
}
📌 Breadcrumb.js
export default function Breadcrumb({ $app, initialState, onClick }) {
this.state = initialState;
this.$target = document.createElement('nav');
this.$target.className = 'Breadcrumb';
$app.appendChild(this.$target);
this.onClick = onClick;
this.setState = nextState => {
this.state = nextState;
this.render();
}
this.render = () => {
console.log("호출");
this.$target.innerHTML =
`<div class="nav-item">root</div>${
this.state.map(
(node, index) =>
`<div class="nav-item" data-index="${index}">${node.name}</div>`
).join('')}`;
}
this.$target.addEventListener('click', (e) => {
const $navItem = e.target.closest('.nav-item');
if ($navItem) {
const { index } = $navItem.dataset;
this.onClick(index ? parseInt(index, 10) : null);
}
})
this.render();
}
📌 ImageView.js
const IMAGE_PATH_PREFIX = 'https://fe-dev-matching-2021-03-serverlessdeploymentbuck-t3kpj3way537.s3.ap-northeast-2.amazonaws.com/public'
export default function ImageView({ $app, initialState, onClick }) {
this.state = initialState;
this.$target = document.createElement('div');
this.$target.className = 'Modal ImageView';
$app.appendChild(this.$target);
this.onClick = onClick;
this.setState = (nextState) => {
this.state = nextState;
this.render();
}
this.render = () => {
console.log(this.state);
this.$target.innerHTML = `<div class="content">${this.state ? `<img src="${IMAGE_PATH_PREFIX}${this.state}">` : '' }</div>`
this.$target.style.display = this.state ? 'block' : 'none';
}
this.$target.addEventListener('click', (e) => {
this.$target.style.display = 'none';
})
this.render();
}
📌 api.js
const API_END_POINT = 'https://zl3m4qq0l9.execute-api.ap-northeast-2.amazonaws.com/dev';
export const request = async (nodeId) => {
try {
const res = await fetch(`${API_END_POINT}/${nodeId ? nodeId : ''}`);
if (!res.ok) {
throw new Error('서버의 상태가 이상합니다!');
}
return await res.json();
} catch (e) {
throw new Error(`무언가 잘못 되었습니다! ${e.message}`);
}
}
📌 Loading.js
export default function Loading({ $app, initialState }) {
this.state = initialState;
this.$target = document.createElement('div');
this.$target.className = "Loading Modal";
$app.appendChild(this.$target);
this.setState = (nextState) => {
this.state = nextState;
this.render();
}
this.render = () => {
this.$target.innerHTML = `<div class="content"><img src="./assets/nyan-cat.gif"></div>`;
this.$target.style.display = this.state ? 'block' : 'none';
}
this.render();
}
728x90