본문 바로가기

비개발자의 개발 일지

현장 팀장에서 수출 ERP까지 — AI와 함께 시스템을 만든 기록

시리즈 보기
php

PHP PDO DELETE + 전체 목록 — GET 파라미터 · isset · query() 완전 정리

by 왕진 2026. 7. 8.
반응형

 

 

Web Development · PHP PDO

PHP PDO DELETE + 전체 목록 — GET 파라미터 · isset · query() 완전 정리

한 파일(list.php)에서 GET 파라미터로 삭제 요청을 처리하고, 나머지 전체 목록을 SELECT해 출력하는 액션 통합 패턴
시작 오늘 분석할 코드

list.php는 두 가지 역할을 동시에 한다. URL에 ?action=delete&id=3이 붙어오면 먼저 해당 회원을 DELETE하고, 이후 항상 전체 회원 목록을 SELECT해 표로 출력한다. 한 파일로 삭제+목록을 처리하는 "액션 통합" 패턴이다.

require_once("MYDB.php"); $pdo = db_connect(); // ① GET 파라미터로 삭제 요청이 왔는지 확인 if (isset($_GET['action']) && $_GET['action'] == 'delete' && $_GET['id'] > 0) { try { $pdo->beginTransaction(); $id = $_GET['id']; $sql = "DELETE FROM member WHERE id = :id"; $stmh = $pdo->prepare($sql); $stmh->bindValue(':id', $id, PDO::PARAM_INT); $stmh->execute(); $pdo->commit(); print "데이터를 " . $stmh->rowCount() . "건 삭제하였습니다."; } catch (PDOException $e) { $pdo->rollBack(); print "오류: " . $e->getMessage(); } } // ② 항상 전체 목록 SELECT (삭제 후에도 실행) try { $sql = "SELECT * FROM member"; $stmh = $pdo->query($sql); // prepare 없이 바로 실행 $count = $stmh->rowCount(); print "검색결과는 " . $count . "건입니다."; } catch (PDOException $e) { print "오류: " . $e->getMessage(); } while($row = $stmh->fetch(PDO::FETCH_ASSOC)) { // 목록 출력 }
실제 출력 미리보기
delete.html 삭제 링크 클릭
list.php?action=delete&id=2
list.php 출력 (id=2 삭제 후)
데이터를 1건 삭제하였습니다.
검색결과는 2건입니다.
번호 이름 연령
1 길동 30
3 철수 25
용어 핵심 용어 정리
$_GETURL 쿼리 문자열 파라미터를 담는 슈퍼글로벌 배열. list.php?action=delete&id=2 → $_GET['action']='delete', $_GET['id']=2
isset()변수 또는 배열 키가 존재하고 null이 아닌지 확인. $_GET['action']이 URL에 없으면 undefined 에러 방지
DELETE FROM테이블에서 행을 삭제하는 SQL. WHERE 없으면 전체 삭제 — 반드시 WHERE id = :id 필수
query()prepare/execute 없이 SQL을 바로 실행하는 PDO 메서드. 외부 입력이 없는 고정 SQL에 한해 사용 가능
액션 통합한 파일에서 GET 파라미터로 액션(delete/edit 등)을 분기해 처리하는 패턴 — 파일 수를 줄임
&& 체인 조건isset() + 값 비교 + id 양수 확인 — 세 조건 모두 참일 때만 DELETE 실행해 오작동 방지
쿼리 문자열URL의 ? 이후 부분. key=value&key2=value2 형식. GET 방식으로 서버에 데이터를 전달
액션 후 목록DELETE 후 바로 전체 목록을 다시 SELECT — 삭제 결과가 즉시 목록에 반영됨

1 isset() + 다중 조건 — 왜 세 가지를 함께 확인하는가

$_GET['action']에 직접 접근하면 URL에 action 파라미터가 없을 때 "Undefined index" 에러가 발생한다. isset()으로 존재 여부를 먼저 확인한 다음에 값 비교를 해야 안전하다. id가 0보다 커야 한다는 조건도 중요하다 — id=0이나 음수는 DB에 존재할 수 없으므로 잘못된 요청을 사전 차단한다.

if (isset($_GET['action']) // ① action 키가 URL에 존재하는가 && $_GET['action'] == 'delete' // ② action 값이 'delete'인가 && $_GET['id'] > 0) { // ③ id가 양수인가 (유효한 ID 범위) // 세 조건 모두 참일 때만 DELETE 실행 }
※ isset()을 && 첫 번째에 쓰는 이유: PHP의 단락 평가(short-circuit evaluation) 덕분에, isset()이 false면 이후 조건을 평가하지 않아 "Undefined index" 에러가 발생하지 않는다.

2 DELETE FROM WHERE — 왜 INSERT와 달리 WHERE가 필수인가

INSERT는 새 행을 추가하므로 WHERE가 없어도 영향 범위가 명확하다(1건 추가). DELETE에서 WHERE를 빠뜨리면 테이블의 모든 행이 삭제된다. 데이터 복구는 백업이 없는 한 불가능하다. WHERE 절에는 반드시 PRIMARY KEY(id)로 대상을 특정해야 한다.

WHERE 없는 DELETE — 전체 삭제
DELETE FROM member
// member 테이블 전체 행 삭제!
// 백업 없으면 복구 불가
WHERE 있는 DELETE — 1건 삭제
DELETE FROM member
WHERE id = :id
// id에 해당하는 행 1건만 삭제

3 query() — prepare 없이 바로 실행하는 이유

SELECT * FROM member는 외부에서 받은 입력값이 전혀 포함되지 않는 고정 SQL이다. SQL Injection 위험이 없으므로 prepare() + bindValue() 없이 query()로 바로 실행할 수 있다. query()는 내부적으로 prepare + execute를 한 번에 처리하고 PDOStatement 객체를 반환한다.

메서드 사용 상황 외부 입력 포함 여부
prepare() + execute() 외부 입력이 SQL에 포함될 때 O — 반드시 bindValue 사용
query() 고정 SQL, 외부 입력 없을 때 X — 직접 실행 가능
⚠ query()에 외부 입력을 직접 이어붙이면 SQL Injection에 무방비 상태가 된다. query("SELECT * FROM member WHERE id = ".$_GET['id']) 형태는 절대 금지.

 

반응형

 

4 액션 통합 패턴 — 왜 한 파일에서 DELETE와 SELECT를 같이 처리하는가

삭제 처리를 별도 파일(delete_process.php)로 분리하면 삭제 후 목록으로 redirect하는 코드가 추가로 필요하다. list.php 한 파일에서 처리하면, 삭제 조건 분기 → 삭제 실행 → 목록 SELECT → 목록 출력이 순서대로 자연스럽게 이어진다. 파일 수가 줄어드는 장점도 있다.

list.php 요청
action=delete? 확인
(있으면) DELETE 실행
항상 SELECT 목록
표로 출력
※ 삭제 후에도 SELECT를 항상 실행하므로, 삭제된 항목이 목록에서 즉시 사라진 상태로 표가 출력된다. 별도 새로고침이 필요 없다.

5 GET vs POST — 왜 삭제 요청에 GET을 쓰는가

이 코드는 단순 링크(<a href="list.php?action=delete&id=1">)로 삭제를 처리한다. 링크는 GET 방식이다. 교육용 코드라 GET을 사용하지만, 실제 서비스에서는 DELETE나 데이터 변경에 GET을 쓰는 것은 좋지 않다. 검색 엔진이 링크를 크롤링하다가 의도치 않게 삭제가 실행될 수 있기 때문이다.

방식 삭제 구현 장단점
GET (이 코드) <a href="?action=delete&id=N"> 구현 간단 / 북마크·크롤링으로 의도치 않은 삭제 위험
POST (권장) <form method="post"> + <button> 폼 제출 필요 / 크롤러·브라우저 뒤로가기에 안전
확인 다이얼로그 JavaScript confirm() 추가 실수 삭제 방지 / JS 비활성화 시 작동 안 함

6 DELETE 후 rowCount() — 왜 삭제 결과를 확인하는가

DELETE 실행 후 rowCount()를 확인하면 실제로 삭제된 행 수를 알 수 있다. 이미 삭제된 id로 다시 DELETE를 요청하면 에러 없이 처리되지만 rowCount()가 0을 반환한다. 이를 통해 "이미 삭제된 데이터"와 "에러로 인한 실패"를 구분할 수 있다.

$stmh->execute(); $pdo->commit(); print "데이터를 " . $stmh->rowCount() . "건 삭제하였습니다."; // 정상 삭제: "데이터를 1건 삭제하였습니다." // 이미 없는 id: "데이터를 0건 삭제하였습니다." (에러 아님)

7 2개의 try-catch 블록 — 왜 분리하는가

DELETE 처리와 SELECT 처리를 하나의 try 블록에 넣으면, DELETE 에러가 났을 때 목록도 출력되지 않는다. 두 try-catch를 분리하면, DELETE가 실패해도 목록은 항상 출력된다. 서로 독립적인 작업이므로 에러 범위도 각각 격리하는 것이 적절하다.

1
첫 번째 try-catch — DELETE 처리action=delete 조건 내부. 삭제 에러가 나도 다음 SELECT로 이어짐
2
두 번째 try-catch — SELECT 전체 목록항상 실행. query()로 전체 목록 조회, 에러 시 별도 메시지
3
while + fetch — 목록 출력SELECT 결과를 한 행씩 꺼내 <TR>로 출력

8 자주 하는 실수
실수 증상 해결
isset() 없이 $_GET['action'] 직접 접근 URL에 action 파라미터 없을 때 "Undefined index" Notice if (isset($_GET['action']) && ...) 순서로 작성
DELETE WHERE 누락 member 테이블 전체 삭제 — 복구 불가 DELETE FROM member WHERE id = :id 반드시 포함
query()에 외부 입력 직접 포함 SQL Injection 취약점 발생 외부 입력이 있으면 반드시 prepare + bindValue 사용
$_GET['id']를 bindValue 없이 SQL에 직접 삽입 SQL Injection — ?id=1 OR 1=1로 전체 삭제 가능 $id = $_GET['id'] 꺼낸 후 bindValue(':id', $id, PARAM_INT)
id > 0 조건 누락 ?id=0 이나 ?id=-1로 요청 시 의미없는 DELETE 실행 시도 && $_GET['id'] > 0 조건 추가로 유효 범위 검증

핵심 한 줄 요약

isset() + && 체인GET 파라미터 존재 확인 + 값 검증 + id 양수 — 세 조건 모두 통과해야 DELETE
DELETE FROM WHEREWHERE 없으면 전체 삭제 — id로 대상 1행만 특정해야 안전
query()외부 입력 없는 고정 SQL은 prepare 없이 query()로 바로 실행 가능
액션 통합 패턴한 파일에서 GET action 분기 → 삭제 처리 → 목록 출력 순서로 이어짐
2개 try-catch 분리DELETE 에러 범위와 SELECT 에러 범위를 격리 — 하나 실패해도 다른 것은 실행
GET으로 삭제교육용 단순 패턴 — 실제 서비스는 POST + 확인 다이얼로그 권장

Tags

#PHP #PDO #DELETE #GET파라미터 #isset #$_GET #query #SELECT #전체목록 #액션처리 #MySQL #PHP학습 #웹개발 #티스토리
▼ 티스토리 태그 입력란 복사용
PHP, PDO, DELETE, GET파라미터, isset, $_GET, query, SELECT, 전체목록, 액션처리
반응형

댓글