프로그래밍 예제

간단한 활용 예제(포토 앨범)

goshek 2024. 8. 22. 12:48

html

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>포토 앨범: TS 비동기 작업</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <div id="app">
    <div id="photo-container">
      <!--
        하위 요소로 외부 API에서 이미지를 첨부
        : class 속성(photo-item)
      -->
    </div>
    <div id="button-container">
      <button id="prev-button">Previous</button>
      <button id="next-button">Next</button>
    </div>
  </div>
 
  <script src="./main.js"></script>
</body>
</html>

 

css

body {
    background-color: #f4f4f9;
    color: #333;
    margin: 0;
    padding: 20px;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
  }
 
  #app {
    background-color: #fff;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
    width: 100%;
    max-width: 600px;
 
    /* app 내부에서 크기가 커질 경우 생기는 스크롤 제거 */
    overflow: hidden;
    border-radius: 10px;
    text-align: center;
  }
 
  #photo-container {
    display: grid;
    /* 컨테이너 내부에 요소가 2열의 공간이 같은 비율로 할당 */
    grid-template-columns: repeat(2, 1fr);
    gap: 15px;
    padding: 20px;
  }
 
  #prev-button, #next-button {
    background-color: #007bff;
    color: #fff;
    border: none;
    padding: 10px 20px;
    margin: 10px;
    border-radius: 5px;
    font-size: 16px;
    cursor: pointer;
    transition: background-color 0.3s ease;
  }
 
  #prev-button:hover, #next-button:hover {
    background-color: #0056b3;
  }
 
  #button-container {
    padding: 10px;
    display: flex;
    justify-content: space-between;
  }
 
  .photo-item {
    border: 1px solid #ddd;
    border-radius: 5px;
    padding: 10px;
    background-color: #fff;
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    text-align: center;
  }
 
  .photo-item img {
    max-width: 100%;
    height: auto;
    border-radius: 5px;
    margin-bottom: 10px;
  }
 
  .photo-item p {
    font-weight: bold;
    color: #555;
    margin: 0;
  }

 

js

/*
! 외부 API를 사용하는 비동기 작업 프로젝트
- jsonplaceholder의 photos 데이터를 사용

>> URL을 통한 데이터 요청 시
>> URL경로?키=쌍의 형태로 옵션 전달 가능

# 요구 사항 정리
- 각 페이지에 photos 데이터의 사진을 4개씩 첨부
  (50 === (4 * 12) + 2)
- 각 사진의 이름 (title)은 사진 아래 작성
- 페이지 간의 이동은 버튼(Previous, Next)으로 이동

- async/await를 사용하여 작성
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
// 현재 페이지 번호를 추적하는 변수 선언 (1로 초기화)
var currentPage = 1;
// 한 페이지에 표시할 사진의 수
var photoPerPage = 4;
//? 지정된 페이지의 사진들을 비동기적으로 불러오는 함수 선언
function fetchPhotos(page) {
    return __awaiter(this, void 0, void 0, function () {
        var response, photos, renderPhotos_1, error_1;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    _a.trys.push([0, 3, , 4]);
                    return [4 /*yield*/, fetch("https://jsonplaceholder.typicode.com/photos?albumId=1")];
                case 1:
                    response = _a.sent();
                    if (!response.ok) {
                        throw new Error('데이터를 가져오지 못했습니다.');
                    }
                    return [4 /*yield*/, response.json()];
                case 2:
                    photos = _a.sent();
                    renderPhotos_1 = photos.splice((page - 1) * photoPerPage, photoPerPage);
                    return [2 /*return*/, renderPhotos_1];
                case 3:
                    error_1 = _a.sent();
                    console.error('Failed');
                    return [2 /*return*/, []];
                case 4: return [2 /*return*/];
            }
        });
    });
}
//? 사진을 페이지에 렌더링하는 함수
// : 4개씩 나누어진 사진 배열을 매개변수로 받아옴
function renderPhotos(photos) {
    // 사진을 표시할 HTML 요소
    var container = document.getElementById('photo-container');
    container.innerHTML = '';
    // 각 사진에 대한 HTML 요소 생성
    photos.forEach(function (photo) {
        var photoElement = document.createElement('div');
        photoElement.className = 'photo-item';
        photoElement.innerHTML = "\n      <img src='".concat(photo.thumbnailUrl, "' alt='").concat(photo.title, "' />\n      <p>").concat(photo.title, "</p>\n      ");
        container.appendChild(photoElement);
    });
}
//! 버튼 이벤트 등록
// 이전 버튼 이벤트
document.getElementById('prev-button').addEventListener('click', function () {
    // 현재 페이지가 1보다 크면 이전 페이지로 이동 (로직 실행)
    if (currentPage > 1) {
        currentPage--;
        updatePhotos();
    }
});
// 다음 버튼 이벤트
document.getElementById('next-button').addEventListener('click', function () {
    // 다음 페이지로 이동
    currentPage++;
    updatePhotos();
});
// 다음 버튼 이벤트
//? 전체 프로젝트 실행 (사진 불러오기)
function updatePhotos() {
    return __awaiter(this, void 0, void 0, function () {
        var photos;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, fetchPhotos(currentPage)];
                case 1:
                    photos = _a.sent();
                    // 4개의 사진을 렌더링하는 함수 호출
                    renderPhotos(photos);
                    return [2 /*return*/];
            }
        });
    });
}
updatePhotos();