728x90

지역 테이블을 작업하다 보면 서울특별시 처럼 full name 으로 쓰기도 하고  서울 처럼 short name 으로 줄여서 쓰는 경우도 있어서 해당 부분에 맞게 데이터를 업데이트 해야할 일이 종종 생긴다. 

 

아래 쿼리문은 그런 경우 테이블 전체에서 수정해야 하는 쿼리문을 한번에 update 할 수 있다. 업데이트 구문에서도 case 구분을 잘쓰면 유용하다. 

UPDATE code_bjd_short
SET kf_name = CASE
    WHEN kf_name LIKE '서울특별시%' THEN REPLACE(kf_name, '서울특별시', '서울')
    WHEN kf_name LIKE '부산광역시%' THEN REPLACE(kf_name, '부산광역시', '부산')
    WHEN kf_name LIKE '대구광역시%' THEN REPLACE(kf_name, '대구광역시', '대구')
    WHEN kf_name LIKE '인천광역시%' THEN REPLACE(kf_name, '인천광역시', '인천')
    WHEN kf_name LIKE '광주광역시%' THEN REPLACE(kf_name, '광주광역시', '광주')
    WHEN kf_name LIKE '대전광역시%' THEN REPLACE(kf_name, '대전광역시', '대전')
    WHEN kf_name LIKE '울산광역시%' THEN REPLACE(kf_name, '울산광역시', '울산')
    WHEN kf_name LIKE '세종특별자치시%' THEN REPLACE(kf_name, '세종특별자치시', '세종')
    WHEN kf_name LIKE '경기도%' THEN REPLACE(kf_name, '경기도', '경기')
    WHEN kf_name LIKE '강원도%' THEN REPLACE(kf_name, '강원도', '강원')
    WHEN kf_name LIKE '충청북도%' THEN REPLACE(kf_name, '충청북도', '충북')
    WHEN kf_name LIKE '충청남도%' THEN REPLACE(kf_name, '충청남도', '충남')
    WHEN kf_name LIKE '전라북도%' THEN REPLACE(kf_name, '전라북도', '전북')
    WHEN kf_name LIKE '전라남도%' THEN REPLACE(kf_name, '전라남도', '전남')
    WHEN kf_name LIKE '경상북도%' THEN REPLACE(kf_name, '경상북도', '경북')
    WHEN kf_name LIKE '경상남도%' THEN REPLACE(kf_name, '경상남도', '경남')
    WHEN kf_name LIKE '제주특별자치도%' THEN REPLACE(kf_name, '제주특별자치도', '제주')
    ELSE kf_name
END;
728x90
728x90

vuex 를 사용해서 store 에 저장하고 여러페이지에서 동일하게 사용하기 예시

aa 라는 변수를 예로 든다면 state 에 초기값을 설정해 두고 mutations 과 actions 으로 값을 변경 할수도 있고 값을 참조 할때는 store.state.aa 로  가져와서 사용 할 수 있다. 

 

vue 에는 세션이 없기에  의외로 많이 사용하는 방법이라 참고해 두면 좋겠다. 

// store.js
import { createStore } from 'vuex';

const store = createStore({
    state: {
        aa: null, // 초기값
    },
    mutations: {
        setAa(state, value) {
            state.aa = value; // 상태 변경
        },
    },
    actions: {
        updateAa({ commit }, value) {
            commit('setAa', value); // 액션을 통해 상태 업데이트
        },
    },
});

export default store;
<template>
    <div>
        <h1>현재 aa 값: {{ aa }}</h1>
        <button @click="changeValue">값 변경</button>
    </div>
</template>

<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';

// Vuex 스토어 사용
const store = useStore();

// aa 값을 가져옵니다.
const aa = computed(() => store.state.aa);

// aa 값을 변경하는 메서드
const changeValue = () => {
    const newValue = Math.random(); // 새로운 랜덤 값
    store.dispatch('updateAa', newValue); // Vuex 액션 호출
};
</script>

 

<template>
    <div>
        <h1>bb.vue에서 aa 값: {{ aa }}</h1>
    </div>
</template>

<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';

// Vuex 스토어 사용
const store = useStore();

// aa 값을 가져옵니다.
const aa = computed(() => store.state.aa);
</script>
728x90
728x90
<div class="input_wrap input_item_area">
  <select class="select" onchange="num(this.value)">
    <option value="1">평수로 입력</option>
    <option value="2">가로 X 세로로 입력</option>
    <option value="3">㎡로 입력</option>
  </select>
  
  <!-- 평수로 입력 -->
  <div class="wrap" id="1">
    <div>
      <input type="text" class="input" placeholder="10" id="areaInput" oninput="convertToSquareMeters()"> <span class="txt">평</span>
    </div>
  </div>
  
  <!-- 가로세로로 입력 -->
  <div class="wrap" style="display: none;" id="2">
    <div>
      <span class="txt">가로</span>
      <input type="text" class="input" placeholder="6.8" id="widthInput" oninput="calculateArea()">
      <span class="txt">m</span>
    </div>
    <div class="divider">
      <span class="txt">세로</span>
      <input type="text" class="input" placeholder="7.15" id="heightInput" oninput="calculateArea()">
      <span class="txt">m</span>
    </div>
    <div>
      <span class="txt">총 면적</span>
      <input type="text" class="input" id="squareMeterInput" readonly> <span class="txt">㎡</span>
    </div>
  </div>
  
  <!-- ㎡로 입력 -->
  <div class="wrap" style="display:none" id="3">
    <div>
      <input type="text" class="input" placeholder="48.6" id="squareMeterInput2" oninput="convertToPyeong()"> <span class="txt">㎡</span>
    </div>
  </div>
</div>
function num(value) {
  // 모든 입력 필드를 숨김
  document.getElementById('1').style.display = 'none';
  document.getElementById('2').style.display = 'none';
  document.getElementById('3').style.display = 'none';

  // 선택한 값에 따라 해당 입력 필드 보이기
  document.getElementById(value).style.display = 'block';
}

function calculateArea() {
  const width = parseFloat(document.getElementById('widthInput').value) || 0;
  const height = parseFloat(document.getElementById('heightInput').value) || 0;
  const areaInSquareMeters = width * height;
  
  // 평으로 변환 (1평 = 3.3㎡)
  const areaInPyeong = areaInSquareMeters / 3.3; // 제곱미터를 평으로 변환
  
  // 평수 입력란에 결과 표시
  document.getElementById('areaInput').value = areaInPyeong.toFixed(2);
  
  // 제곱미터 입력란에 결과 표시
  document.getElementById('squareMeterInput').value = areaInSquareMeters.toFixed(2);
}

function convertToPyeong() {
  const squareMeters = parseFloat(document.getElementById('squareMeterInput2').value) || 0;
  
  // 평으로 변환 (1평 = 3.3㎡)
  const areaInPyeong = squareMeters / 3.3; // 제곱미터를 평으로 변환
  
  // 평수 입력란에 결과 표시
  document.getElementById('areaInput').value = areaInPyeong.toFixed(2);
}

function convertToSquareMeters() {
  const pyeong = parseFloat(document.getElementById('areaInput').value) || 0;
  
  // 제곱미터로 변환 (1평 = 3.3㎡)
  const areaInSquareMeters = pyeong * 3.3; // 평을 제곱미터로 변환
  
  // 제곱미터 입력란에 결과 표시
  document.getElementById('squareMeterInput').value = areaInSquareMeters.toFixed(2);
}
728x90
728x90

1. 비동기 처리

문제: 주어진 두 개의 비동기 함수 fetchData1과 fetchData2를 호출하여 그 결과를 배열로 반환하는 fetchAllData 함수를 작성하세요. 두 함수는 각각 1초 후에 값을 반환합니다.

 

function fetchData1() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('Data from fetchData1');
        }, 1000);
    });
}

function fetchData2() {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve('Data from fetchData2');
        }, 1000);
    });
}

async function fetchAllData() {
    // 여기에 코드를 작성하세요.
}

 

정답 

async function fetchAllData() {
    const results = await Promise.all([fetchData1(), fetchData2()]);
    return results;
}

 

2. 배열 및 객체조작

문제: 주어진 배열에서 중복된 값을 제거하고, 각 값의 출현 횟수를 객체 형태로 반환하는 countOccurrences 함수를 작성하세요.

function countOccurrences(arr) {
    // 여기에 코드를 작성하세요.
}

 

정답

function countOccurrences(arr) {
    return arr.reduce((acc, item) => {
        acc[item] = (acc[item] || 0) + 1;
        return acc;
    }, {});
}
728x90
728x90

php 에서도 가능하고 npm 으로도 설치가 가능해서 vue 나 react 에서도 가능한 스크립트다. 

 

상단에 아래와 같이 스크립트 url 을 추가해 주고 인쇄 되어야 하는 content 같은 div 아이디 값을 지정해 주고 .save 를 주면 pdf 로 다운로드가 가능하다. pdf 다운로드는 실제 변환되는 걸로 코딩을 하려면 무척 시간이 많이 걸리고 css 스타일도 inline 으로 밖아야 해서 코드도 무척 길어 지는데 아래와 같은 스크립트는 특정 div 만도 가능해서 좋은거 같다. 

<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>

 

pdf 다운로드 기능이 필요 하다면 참고해 보면 좋겠다.

var element = document.getElementById('content'); // PDF로 변환할 HTML 요소
html2pdf()
    .from(element)
    .save('document.pdf'); // 저장할 파일 이름

 

npm install html2pdf.js

 

728x90
728x90

다중으로 보내야 하는 경우 [] 괄호로 배열로 보내곤 하는데 이때 [] 괄호 안에 유니크한 번호를 넣으면 해당 번호를 기준으로 그룹 단위로 데이터를 테이블에 쌓을 수 있다. 요즘은 json 형태의 트리 구조로 데이터를 많이 잡기도 해서 조금 예스러운 코딩 일 수 있지만 종종 필요할 때가 있으니 참고하면 좋겠다. 

 

 

아래는 form 태그와 php 백 단에서 어떻게 처리되는지 에시를 보여준다.

<form method="POST" action="/your-endpoint">
    <table>
        <tr>
            <th>엘리베이터 여부</th>
            <td>
                <div class="com_chk__wrap">
                    <input type="hidden" name="cost_type[0]" value="189"> <!-- categories 189 : 엘리베이터여부 -->
                    <label><input type="radio" name="elevator_avail[0]" value="y">있음</label>
                    <label><input type="radio" name="elevator_avail[0]" value="n">없음 (있으나 사용 못 함)</label>
                </div>
            </td>
            <td class="td_center"><input type="number" name="labor_cost[0]" class="input num" value=""></td>
            <td class="td_center"><input type="number" name="margin_cost[0]" class="input num" value=""></td>
            <td class="td_center"><span class="">30,000원</span></td>
        </tr>
        <tr>
            <th>짐 여부</th>
            <td>
                <div class="com_chk__wrap">
                    <input type="hidden" name="cost_type[1]" value="188"> <!-- categories 188 : 짐 여부 -->
                    <label><input type="radio" name="luggage_avail[1]" value="y">있음</label>
                    <label><input type="radio" name="luggage_avail[1]" value="n">없음 (있으나 사용 못 함)</label>
                </div>
            </td>
            <td class="td_center"><input type="number" name="labor_cost[1]" class="input num" value=""></td>
            <td class="td_center"><input type="number" name="margin_cost[1]" class="input num" value=""></td>
            <td class="td_center"><span class="">30,000원</span></td>
        </tr>
        <!-- 추가적인 tr 요소들... -->
    </table>
    <button type="submit">제출</button>
</form>
<?php
// 데이터베이스 연결
$mysqli = new mysqli("localhost", "username", "password", "database");

// 연결 확인
if ($mysqli->connect_error) {
    die("Connection failed: " . $mysqli->connect_error);
}

// POST 데이터 수신
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 각 항목 반복 처리
    foreach ($_POST['cost_type'] as $index => $costType) {
        // 엘리베이터 여부
        $availKey = 'elevator_avail[' . $index . ']';
        $laborCostKey = 'labor_cost[' . $index . ']';
        $marginCostKey = 'margin_cost[' . $index . ']';

        $avail = isset($_POST[$availKey]) ? $mysqli->real_escape_string($_POST[$availKey]) : '';
        $laborCost = isset($_POST[$laborCostKey]) ? (int)$_POST[$laborCostKey] : 0;
        $marginCost = isset($_POST[$marginCostKey]) ? (int)$_POST[$marginCostKey] : 0;
        $totalCost = $laborCost + $marginCost;

        // INSERT 쿼리 작성
        $query = "INSERT INTO estimate_extra_cost (est_number, cost_type, add_postion, title, title_code, etc1, labor_cost, margin_cost, total_cost) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";

        // 준비된 문을 사용하여 실행
        $stmt = $mysqli->prepare($query);

        // 필요한 값을 설정
        $estNumber = 'E001'; // 실제 사용하려는 값으로 변경
        $addPosition = 'etc'; // 필요에 따라 조정
        $title = ($costType == 189) ? '엘리베이터 여부' : '짐 여부'; // 조건에 따라 제목 설정
        $titleCode = ($costType == 189) ? 'CATEGORY_ELEVATOR' : 'CATEGORY_LUGGAGE'; // 카테고리 코드 설정

        // 문장에 파라미터 바인딩
        $stmt->bind_param("sssssiiiii", $estNumber, $costType, $addPosition, $title, $titleCode, $avail, $laborCost, $marginCost, $totalCost);
        $stmt->execute();
    }

    // 준비된 문 종료
    $stmt->close();
}

// 데이터베이스 연결 종료
$mysqli->close();
?>
728x90
728x90

프로그램 작업을 하다 보면 견적서 같은 작업에서 견적서 번호를 유니크하게 생성해야 하는 경우가 종종 있다. 고객사에서는 번호를 한달에 몇건을 체크하고 싶어서 순차적으로 증가되게 해달라고 요청하시는 경우가 종종 있다. 그래서 주로 날짜와 뒷번호는 순번으로 채워지곤 하는데 아래 예시는 일자별 견적서 번호를 유니크하게 생성하는 로직이다. 

 

이때는 테이블 하나에서 해결하려고 하면 많이 복잡해 지니 차라리 번호만 관리하는 테이블을 하나 더 만드는것이 좋다. 또한 다른사람이 동일하게 접속해서 들어가더라도 순서대로 번호가 증가해서 그 키값을 가지고 있는거라 예약번호나 견적서 번호 같은걸 생성 해야 할때 참고 하면 좋겠다.  

CREATE TABLE temp_estimate_numbers (
    id INT AUTO_INCREMENT PRIMARY KEY,
    est_number VARCHAR(20) NOT NULL UNIQUE
);

 

테이블 쿼리문과 번호 생성하는 코드를 php 로 아래와 같이 메모해 본다. 

$pdo = dbconn(); // 데이터베이스 연결

// 견적서 번호 생성
$date = date('ymd');
$max_number = QRY_MAX("temp_estimate_numbers", " AND est_number LIKE '$date%'", "est_number");

if ($max_number) {
    $sequence = intval(substr($max_number, -3)) + 1; // 마지막 3자리에서 순번 증가
} else {
    $sequence = 1; // 기존 주문이 없으면 1로 시작
}

$orderNumber = str_pad($sequence, 3, '0', STR_PAD_LEFT);
$estimateNumber = $date . '-' . $orderNumber;

// 임시 테이블에 저장
$insert_sql = "INSERT INTO temp_estimate_numbers (est_number) VALUES ('$estimateNumber')";
$stmt = $pdo->prepare($insert_sql);

// 실행
if ($stmt->execute()) {
    echo "견적서 번호가 성공적으로 저장되었습니다: " . $estimateNumber;
} else {
    echo "저장 중 오류 발생.";
}
728x90
728x90

php 에서 pdf 파일로 다운로드 하는 방법은 몇가지가 있는데 그중 컴포저를 설치 했을 경우의 예시를 들어 본다. 아래와 같이 tecnickcom/tcpdf 로 컴포저를 설치하고 적용 방법은 아래에 3가지 경우로 예시를 메모해 본다. 

 

 

보통은 window.print() 로 날리면 해당 화면이 출력하기 모듈이 뜨면서 거기서 pdf 로 바로 다운로드가 가능한데 pdf 로 바로 다운이 되어야 하는 경우는 아래와 같이 수정을 해야 한다.  

composer require tecnickcom/tcpdf

 

1. tcpdf 를 사용해서 $pdf 로 생성하는 방법 

<?php
require_once('vendor/autoload.php');

// PDF 문서 생성
$pdf = new TCPDF();

// 문서 정보 설정
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('Your Name');
$pdf->SetTitle('견적서');
$pdf->SetSubject('견적서 예제');
$pdf->SetKeywords('TCPDF, PDF, example, test, guide');

// 페이지 추가
$pdf->AddPage();

// 제목 추가
$pdf->SetFont('helvetica', 'B', 20); // 제목 폰트 설정
$pdf->Cell(0, 10, '견적서', 0, 1, 'C');
$pdf->Ln(10); // 줄 바꿈

// 테이블 데이터
$tableData = [
    ['항목', '수량', '단가', '합계'],
    ['상품 A', '2', '50000', '100000'],
    ['상품 B', '1', '30000', '30000'],
    ['상품 C', '3', '20000', '60000'],
];

// 테이블 스타일 설정
$pdf->SetFont('helvetica', 'B', 12); // 헤더 폰트 설정
$pdf->SetFillColor(200, 220, 255); // 헤더 배경 색상
$pdf->Cell(40, 10, $tableData[0][0], 1, 0, 'C', 1);
$pdf->Cell(40, 10, $tableData[0][1], 1, 0, 'C', 1);
$pdf->Cell(40, 10, $tableData[0][2], 1, 0, 'C', 1);
$pdf->Cell(40, 10, $tableData[0][3], 1, 1, 'C', 1);

// 테이블 데이터 출력
$pdf->SetFont('helvetica', '', 12); // 데이터 폰트 설정
$pdf->SetFillColor(255, 255, 255); // 데이터 배경 색상

foreach ($tableData as $key => $row) {
    if ($key > 0) { // 첫 번째 행은 이미 출력했으므로 생략
        $pdf->Cell(40, 10, $row[0], 1, 0, 'C', 1);
        $pdf->Cell(40, 10, $row[1], 1, 0, 'C', 1);
        $pdf->Cell(40, 10, $row[2], 1, 0, 'C', 1);
        $pdf->Cell(40, 10, $row[3], 1, 1, 'C', 1);
    }
}

// 하단에 직인 이미지 추가
$pdf->Ln(10);
$imageFile = 'path/to/your/seal.png'; // 직인 이미지 경로
$pdf->Image($imageFile, 150, 250, 40, 40, 'PNG', '', '', false, 300, '', false, false, 0, false, false, false);

// PDF 파일 다운로드
$pdf->Output('견적서.pdf', 'D');
?>

 

2. html 태그를 css 파일을 적용해서 중간에 추가 하는 경우

<?php
require_once('vendor/autoload.php');

// PDF 문서 생성
$pdf = new TCPDF();

// 문서 정보 설정
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('Your Name');
$pdf->SetTitle('견적서');
$pdf->SetSubject('견적서 예제');
$pdf->SetKeywords('TCPDF, PDF, example, test, guide');

// 페이지 추가
$pdf->AddPage();

// CSS 파일 포함
$css = file_get_contents('path/to/styles.css'); // CSS 파일 경로
$html = '
<style>' . $css . '</style>
<h1>견적서</h1>
<table>
    <thead>
        <tr>
            <th>항목</th>
            <th>수량</th>
            <th>단가</th>
            <th>합계</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>상품 A</td>
            <td>2</td>
            <td>50000</td>
            <td>100000</td>
        </tr>
        <tr>
            <td>상품 B</td>
            <td>1</td>
            <td>30000</td>
            <td>30000</td>
        </tr>
        <tr>
            <td>상품 C</td>
            <td>3</td>
            <td>20000</td>
            <td>60000</td>
        </tr>
    </tbody>
</table>
<br>
<img src="path/to/your/seal.png" alt="직인" width="100" height="100" style="float:right;">
';

// HTML 콘텐츠 출력
$pdf->writeHTML($html, true, false, true, false, '');

// PDF 파일 다운로드
$pdf->Output('견적서.pdf', 'D');
?>

 

3. html 코드가 있는 php 파일을 include 해서 적용하는 방법

<?php
require_once('vendor/autoload.php');

// PDF 문서 생성
$pdf = new TCPDF();

// 문서 정보 설정
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('Your Name');
$pdf->SetTitle('견적서');
$pdf->SetSubject('견적서 예제');
$pdf->SetKeywords('TCPDF, PDF, example, test, guide');

// 페이지 추가
$pdf->AddPage();

// HTML 파일 포함
ob_start(); // 출력 버퍼링 시작
include('aa.php'); // aa.php 파일 포함
$html = ob_get_clean(); // 버퍼 내용을 변수로 저장하고 버퍼 비우기

// HTML 콘텐츠 출력
$pdf->writeHTML($html, true, false, true, false, '');

// PDF 파일 다운로드
$pdf->Output('견적서.pdf', 'D');
?>

 

각 상황에 맞게 사용하는 것이 좋겠다. 컴포저 설치시에는 실서버와 동일한 php 버전에서 컴포저를 설치해야 버전 이슈가 없는 점은 참고해야 한다. 

728x90
728x90

약관동의 체크박스는 보기에는 간단한데 의외로 체크해야할 로직이 많다. 전체 동의를 하면 하위 체크박스가 모두 선택 되어야 하고 하나가 풀리면 전체동의 체크박스도 풀려야 하고 필수 동의 체크값에 따라 하위 버튼도 disabled 가 해제 되어야 해서 체크박스가 선택 될때마다 하단 등록 버튼 상태를 변경하는 로직은 한번 정리해두는게 좋다. 

 

아래 예시는 하단에 확인 버튼의 disabled 속성을 isConfirmDisabled 변수에 담고 체크박스의 값에 따라 watch 에서 체크 하는 로직으로 구현 하였다. 체크박스 상태가 변경 될때마다 확인이 되어야 하는 부분이어서 watch 로 걸어 주는것이 조금 더 편리하다. vue 에서 watch 는 너무 많이 걸면 조건이 꼬이기도 하니까 주의할 필요가 있다.  

<template>
  <CheckField
    v-for="(term, index) in terms"
    :key="index"
    v-model="term.agreed"
    :class="'terms-item'"
    :label="term.text"
    :required-indicator="term.required"
    :optional-indicator="!term.required"
    :show-view-button="index !== 0"
    :toggle-checkbox="index === 3 ? toggleMarketingConsent : () => {}"
    @view="() => clickConsent(term.content, index)"
  />
  <button :disabled="isConfirmDisabled">확인</button>
</template>

<script setup>
import { ref, watch } from 'vue';

const terms = ref([
  { agreed: false, text: '항목 1', required: true, content: '내용 1' },
  { agreed: false, text: '항목 2', required: true, content: '내용 2' },
  { agreed: false, text: '항목 3', required: false, content: '내용 3' },
  { agreed: false, text: '마케팅 동의', required: false, content: '내용 4' },
]);

const isConfirmDisabled = ref(true);

// 모든 항목의 동의 상태를 체크
watch(
  () => terms.value.map(term => term.agreed),
  (newAgreements) => {
    const allAgreed = terms.value[0]?.agreed && 
                      terms.value[1]?.agreed && 
                      terms.value[2]?.agreed;

    isConfirmDisabled.value = !allAgreed; // 모두 동의하지 않으면 버튼 비활성화
  },
  { immediate: true } // 컴포넌트 마운트 시 즉시 체크
);
</script>
728x90
728x90

쿼리문에서 되도록 group by 를 안쓰는 구조로 테이블구조를 잡는것이 좋지만 의외로 group by 를 해야 하는 경우가 꽤 많이 발생한다. 이때 그룹핑을 했을때 페이징 관련 처리 방법을 php 코드를 예시로 아래와 같이 정리해 본다. 

 

 

group by 예시 쿼리문

SELECT column1, COUNT(*)
FROM your_table
GROUP BY column1
ORDER BY COUNT(*) DESC
LIMIT 10;

 

group by 로 php 코드에서 페이징 처리 예시

<?php
// 데이터베이스 연결 설정
$host = 'localhost';
$dbname = 'your_database';
$username = 'your_username';
$password = 'your_password';

try {
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    echo "Connection failed: " . $e->getMessage();
}

// 페이지 번호와 페이지당 항목 수 설정
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1; // 현재 페이지
$limit = 10; // 페이지당 항목 수
$offset = ($page - 1) * $limit; // OFFSET 계산

// 총 게시물 수를 가져오기
$totalQuery = $pdo->query("SELECT COUNT(*) FROM board");
$totalRows = $totalQuery->fetchColumn();
$totalPages = ceil($totalRows / $limit); // 총 페이지 수

// 데이터 가져오기
$query = $pdo->prepare("SELECT * FROM board ORDER BY created_at DESC LIMIT :offset, :limit");
$query->bindParam(':offset', $offset, PDO::PARAM_INT);
$query->bindParam(':limit', $limit, PDO::PARAM_INT);
$query->execute();
$posts = $query->fetchAll(PDO::FETCH_ASSOC);

// 결과 출력
foreach ($posts as $post) {
    echo "<h2>" . htmlspecialchars($post['title']) . "</h2>";
    echo "<p>" . htmlspecialchars($post['content']) . "</p>";
}

// 페이지 네비게이션
echo '<div class="pagination">';
if ($page > 1) {
    echo '<a href="?page=' . ($page - 1) . '">이전</a>';
}

for ($i = 1; $i <= $totalPages; $i++) {
    if ($i == $page) {
        echo '<strong>' . $i . '</strong>'; // 현재 페이지 표시
    } else {
        echo '<a href="?page=' . $i . '">' . $i . '</a>';
    }
}

if ($page < $totalPages) {
    echo '<a href="?page=' . ($page + 1) . '">다음</a>';
}
echo '</div>';
?>
728x90

+ Recent posts