시작 오늘 분석할 코드 — 전체 구조
list.php는 43-2 패턴의 핵심이다. 단 하나의 파일이 5가지 역할을 수행한다. GET action=delete(삭제), POST action=insert(등록), POST action=update(수정), POST search_key(검색), 아무것도 없으면 전체 목록. session_start()는 UPDATE 처리에서 $_SESSION['id']를 읽기 위해 반드시 필요하다.
<?php session_start(); // UPDATE에서 $_SESSION['id'] 사용 ?> <!-- HTML 헤더, 검색폼 ... --> <?php require_once("MYDB.php"); $pdo = db_connect(); // 1. 삭제 처리 (GET) if(isset($_GET['action']) && $_GET['action'] == 'delete' && $_GET['id'] > 0){ try{ $pdo->beginTransaction(); /* DELETE ... */ $pdo->commit(); } catch{ $pdo->rollBack(); } } // 2. 등록 처리 (POST) if(isset($_POST['action']) && $_POST['action'] == 'insert'){ try{ $pdo->beginTransaction(); /* INSERT ... */ $pdo->commit(); } catch{ $pdo->rollBack(); } } // 3. 수정 처리 (POST) if(isset($_POST['action']) && $_POST['action'] == 'update'){ $id = $_SESSION['id']; try{ $pdo->beginTransaction(); /* UPDATE ... */ $pdo->commit(); } catch{ $pdo->rollBack(); } unset($_SESSION['id']); // 사용한 세션 변수 삭제 } // 4. 검색 OR 전체 목록 if(isset($_POST['search_key']) && $_POST['search_key'] != ""){ // LIKE 검색 }else{ $stmh = $pdo->query("SELECT * FROM member"); } ?>
실제 출력 미리보기 — list.php 화면
검색 결과는 4건입니다.
| 번호 |
성 |
이름 |
연령 |
|
|
| 1 |
홍 |
길동 |
25 |
수정 |
삭제 |
| 2 |
김 |
철수 |
30 |
수정 |
삭제 |
용어 핵심 용어 정리
통합 CRUD 허브하나의 PHP 파일이 여러 액션을 처리 — action 파라미터로 실행할 블록을 분기
$_GET['action']URL 파라미터로 전달된 액션 — 삭제 링크(href)로 전달
$_POST['action']폼 hidden 필드로 전달된 액션 — insert 폼, update 폼 구분
unset($_SESSION['id'])세션에서 특정 키만 제거 — session_destroy()와 달리 다른 세션 데이터 유지
query() vs prepare()외부 입력 없는 고정 SQL은 query() 사용 가능 — 전체 목록 SELECT *가 해당
3개 if 독립 실행if-else가 아닌 독립적인 3개 if — 조건에 해당하는 블록만 실행, 나머지는 건너뜀
search_key 빈값 분기검색어 있으면 LIKE 검색, 없거나 빈 문자열이면 전체 목록 — 동일 페이지에서 전환
while+fetch 목록 렌더링SELECT 결과를 한 행씩 꺼내 TABLE TR로 출력 — PHP+HTML 혼재 패턴
1 session_start() — 왜 list.php에도 필요한가
list.php 자체는 세션에 값을 저장하지 않는다. 그러나 UPDATE 처리 블록에서 $_SESSION['id']를 읽어야 한다. 세션 데이터를 읽으려면 먼저 session_start()로 세션을 재개해야 한다. 저장은 updateform.php에서, 읽기는 list.php에서 — 두 파일 모두 session_start()가 필요하다.
// updateform.php: 세션에 id 저장 session_start(); $_SESSION['id'] = $_GET['id']; // 저장 // list.php: 세션에서 id 읽기 session_start(); // 읽기 전에도 session_start() 필수 $id = $_SESSION['id']; // 읽기 unset($_SESSION['id']); // 사용 후 삭제
⚠ session_start()를 PHP 파일 최상단(HTML 출력 전)에 두지 않으면 "headers already sent" 경고가 발생하고 세션이 작동하지 않는다. `<?php session_start(); ?>`를 파일 첫 줄에 작성하는 것이 안전하다.
2 3가지 액션 분기 — GET과 POST의 차이로 식별
list.php는 PHP 요청이 들어올 때 어떤 작업을 해야 하는지를 파라미터 조합으로 식별한다. 삭제는 GET action=delete, 등록은 POST action=insert, 수정은 POST action=update. 3개 if 블록은 독립적으로(else if가 아님) 평가되어 해당 조건만 실행된다.
| 요청 출처 |
전달 방식 |
PHP에서 읽는 변수 |
실행 블록 |
| 삭제 링크 클릭 (href) |
GET ?action=delete&id=N |
$_GET['action'] == 'delete' |
DELETE 트랜잭션 |
| form.html 등록 폼 submit |
POST (hidden action=insert) |
$_POST['action'] == 'insert' |
INSERT 트랜잭션 |
| updateform.php 수정 폼 submit |
POST (hidden action=update) |
$_POST['action'] == 'update' |
UPDATE 트랜잭션 + unset |
| 검색 폼 submit |
POST search_key=키워드 |
$_POST['search_key'] != "" |
LIKE 검색 SELECT |
| 직접 URL 접속 / 되돌아가기 |
GET (파라미터 없음) |
어떤 조건도 해당 안 됨 |
query() 전체 목록 |
※ if 블록이 if-else가 아닌 독립적인 3개 if인 이유: DELETE 후 목록 표시, INSERT 후 목록 표시가 이어서 실행되어야 하기 때문이다. DELETE if → INSERT if → UPDATE if → 검색/목록 블록 순으로 위에서 아래로 순서대로 실행된다.
3 unset($_SESSION['id']) — session_destroy()와의 차이
UPDATE 처리 후 unset($_SESSION['id'])을 호출한다. 이것은 세션 전체를 파기하는 session_destroy()와 다르다. $_SESSION 배열에서 'id' 키만 제거해 값이 남아 다음 요청에서 잘못된 id로 UPDATE가 실행되는 것을 방지한다.
unset($_SESSION['id']) — 특정 키만 삭제
unset($_SESSION['id']);
// $_SESSION = ['user' => 'admin', ...] 나머지 유지
// 같은 세션의 다른 데이터 보존
// UPDATE 완료 후 id 키만 제거할 때 적합
session_destroy() — 세션 전체 파기
session_destroy();
// $_SESSION = [] — 모든 세션 데이터 삭제
// 로그아웃 시 사용자 정보 전부 초기화
// UPDATE 완료 후 사용 시 로그인 정보 날아감
4 INSERT 블록 — POST 3개 필드 + 트랜잭션
form.html에서 POST로 last_name, first_name, age + hidden action=insert가 전달된다. INSERT 블록은 beginTransaction으로 시작해 3개 bindValue 후 execute, commit으로 완료한다. 실패 시 rollBack.
if(isset($_POST['action']) && $_POST['action'] == 'insert'){ try{ $pdo->beginTransaction(); $sql = "INSERT INTO member (last_name, first_name, age) VALUES (:last_name, :first_name, :age)"; $stmh = $pdo->prepare($sql); $stmh->bindValue(':last_name', $_POST['last_name'], PDO::PARAM_STR); $stmh->bindValue(':first_name', $_POST['first_name'], PDO::PARAM_STR); $stmh->bindValue(':age', $_POST['age'], PDO::PARAM_INT); $stmh->execute(); $pdo->commit(); print "데이터를".$stmh->rowCount()."건 입력하였습니다."; }catch(PDOException $Exception){ $pdo->rollBack(); print "오류:".$Exception->getMessage(); } }
5 UPDATE 블록 — $_SESSION['id']로 안전하게 id 수신
POST로 전달된 데이터(last_name, first_name, age)는 사용자가 폼에서 수정한 값이다. id는 POST로 받지 않고 세션에서 읽는다. POST 데이터는 클라이언트가 조작 가능하지만 세션은 서버가 관리하므로 조작 불가능하다.
if(isset($_POST['action']) && $_POST['action'] == 'update'){ $id = $_SESSION['id']; // updateform.php에서 저장해둔 id try{ $pdo->beginTransaction(); $sql = "UPDATE member SET last_name=:last_name, first_name=:first_name, age=:age WHERE id=:id"; $stmh = $pdo->prepare($sql); $stmh->bindValue(':last_name', $_POST['last_name'], PDO::PARAM_STR); $stmh->bindValue(':first_name', $_POST['first_name'], PDO::PARAM_STR); $stmh->bindValue(':age', $_POST['age'], PDO::PARAM_INT); $stmh->bindValue(':id', $id, PDO::PARAM_INT); $stmh->execute(); $pdo->commit(); }catch(PDOException $Exception){ $pdo->rollBack(); } unset($_SESSION['id']); // try-catch 바깥에서 처리 완료 후 세션 키 삭제 }
6 검색 vs 전체 목록 — prepare vs query 선택 이유
검색어가 있으면 LIKE 검색(외부 입력 포함 → prepare 필수), 없으면 전체 목록(외부 입력 없음 → query 가능). query()는 prepare-execute 2단계를 생략할 수 있지만, 외부 입력값이 SQL에 포함될 때는 절대 사용하면 안 된다.
// 검색어 있을 때 — prepare() 필수 (외부 입력 포함) if(isset($_POST['search_key']) && $_POST['search_key'] != ""){ $search_key = '%'.$_POST['search_key'].'%'; $sql = "SELECT * FROM member WHERE last_name LIKE :last_name OR first_name LIKE :first_name"; $stmh = $pdo->prepare($sql); $stmh->bindValue(':last_name', $search_key); $stmh->bindValue(':first_name', $search_key); $stmh->execute(); }else{ // 검색어 없을 때 — query() 사용 가능 (고정 SQL) $stmh = $pdo->query("SELECT * FROM member"); }
7 목록 출력 — while+fetch와 수정/삭제 링크
SELECT 결과를 while+fetch로 꺼내 TABLE의 TR로 출력한다. 각 행에 수정/삭제 링크를 동적으로 생성한다. 원본 코드에서 href 값에 따옴표가 없는데, HTML5 브라우저는 이를 허용하지만 W3C 표준을 위반한다.
while($row = $stmh->fetch(PDO::FETCH_ASSOC)){ // 수정 링크 — href에 따옴표 없음 (브라우저 허용하나 비표준) "<a href=updateform.php?id=".htmlspecialchars($row['id']). ">수정</a>" // 삭제 링크 — action=delete, & (HTML 속성 안이면 & 권장) "<a href=list.php?action=delete&id=".htmlspecialchars($row['id']). ">삭제</a>" } // 표준 권장 방식 "<a href='updateform.php?id=".htmlspecialchars($row['id'])."'>수정</a>" "<a href='list.php?action=delete&id=".htmlspecialchars($row['id'])."'>삭제</a>"
href에 따옴표 없는 패턴 — 교육 코드의 특이점
- HTML4까지는 속성값에 따옴표가 선택 사항이었으나 HTML5 표준에서도 따옴표 권장
- 특히 URL에 &가 포함되면 따옴표 없는 상태에서 파서 오작동 가능
- 실제 프로젝트에서는 href='...' 또는 href="..." 형태로 따옴표 항상 포함
8 자주 하는 실수
| 실수 |
증상 |
해결 |
| session_start() 누락 |
$_SESSION['id'] 읽기 불가 → UPDATE 블록에서 $id = null → WHERE id = null → 0건 수정 |
list.php 파일 최상단에 session_start() 추가 |
| DELETE/INSERT/UPDATE를 else if로 연결 |
첫 번째 블록 실행 후 나머지 목록 SELECT가 실행 안 됨 |
독립적인 if 3개로 분리 — 목록 SELECT는 별도 블록 |
| unset 대신 session_destroy() 사용 |
로그인 정보 등 다른 세션 변수 전부 삭제됨 |
업데이트 완료 후 unset($_SESSION['id'])만 호출 |
| 전체 목록에도 prepare 사용 (불필요한 복잡성) |
동작은 하나 코드만 길어짐 |
고정 SQL은 query() — prepare는 외부 입력 포함 SQL에만 사용 |
| href의 &를 &로 이스케이프 안 함 |
W3C 유효성 오류, 일부 파서 오작동 |
href 속성 안 & → &로 이스케이프 |
핵심 한 줄 요약
통합 CRUD 허브list.php 하나가 GET action=delete / POST action=insert / POST action=update / LIKE검색 / 전체목록 분기
3개 독립 ifelse if 아님 — 삭제/등록/수정 후 목록 SELECT가 항상 이어서 실행
unset($_SESSION['id'])UPDATE 후 id 키만 제거 — session_destroy()는 전체 세션 파기
query() vs prepare()전체 목록(고정 SQL) → query(), LIKE 검색(외부 입력) → prepare() 필수
$_SESSION['id'] 읽기UPDATE WHERE 조건 id를 세션에서 — POST hidden id보다 안전, 조작 불가
Tags
#PHP #PDO #CRUD #INSERT #UPDATE #DELETE #SELECT #통합허브 #session_start #unset #isset #LIKE검색 #PHP학습 #웹개발
▼ 티스토리 태그 입력란 복사용
PHP, PDO, CRUD, INSERT, UPDATE, DELETE, SELECT, 통합허브, session_start, unset, isset, LIKE검색, PHP학습, 웹개발
댓글