PHP 기술 면접 대비 핵심 개념 정리 🚀

PHP 면접에서 자주 물어보는 모든 것들, 여기서 한방에 정리해 볼게!
📌 PHP 면접, 어떻게 준비해야 할까?
안녕! 오늘은 PHP 기술 면접을 준비하는 개발자 친구들을 위해 핵심 개념을 정리해 볼게. 🤓 PHP 개발자로 취업하려면 기술 면접은 피할 수 없는 관문이지! 특히 요즘 웹 개발 시장에서 PHP는 여전히 중요한 위치를 차지하고 있어서 제대로 알아두면 큰 도움이 될 거야.
웹 개발 실력을 향상시키고 싶다면 재능넷(https://www.jaenung.net)에서 PHP 관련 강의나 멘토링을 찾아보는 것도 좋은 방법이야. 다양한 개발자들이 자신의 지식을 공유하고 있으니 참고해봐! 자, 이제 본격적으로 PHP 면접 준비를 시작해 보자! 🚀
1. PHP 기본 개념 및 특징 🧩
1.1 PHP란 무엇인가?
PHP(PHP: Hypertext Preprocessor)는 서버 사이드 스크립트 언어야. 웹 개발에 특화되어 있고, HTML에 쉽게 삽입할 수 있어. 1994년 라스무스 러도프(Rasmus Lerdorf)가 처음 만들었고, 지금은 PHP 8.3까지 발전했어! 🚀
🔍 면접 예상 질문: "PHP의 특징과 다른 언어와의 차이점은 무엇인가요?"
💡 답변 포인트:
- 서버 사이드 스크립트 언어로, 클라이언트에게 코드가 노출되지 않음
- 웹 개발에 최적화된 다양한 내장 함수 제공
- 대부분의 데이터베이스와 쉽게 연동 가능
- 크로스 플랫폼 지원 (Windows, Linux, macOS 등)
- 오픈 소스이며 무료로 사용 가능
1.2 PHP의 역사와 버전별 주요 특징
PHP는 계속해서 발전해 왔어. 최신 버전인 PHP 8.3은 2023년 11월에 출시됐고, 성능과 기능이 크게 향상됐지. 면접에서 PHP 버전별 차이점을 아는 것은 꽤 중요한 포인트야! 😉
버전 | 출시년도 | 주요 특징 |
---|---|---|
PHP 5 | 2004 | 향상된 OOP 지원, PDO 도입 |
PHP 7 | 2015 | 성능 2배 향상, 타입 힌팅, null 병합 연산자 |
PHP 8.0 | 2020 | JIT 컴파일러, 명명된 인수, 속성(Attributes) |
PHP 8.1 | 2021 | Fibers, 열거형, 읽기 전용 속성 |
PHP 8.2 | 2022 | Readonly 클래스, null/false/true 타입 |
PHP 8.3 | 2023 | 타입 시스템 개선, 성능 최적화, json_validate() 함수 |
1.3 PHP의 동작 원리
PHP가 어떻게 동작하는지 이해하는 것은 면접에서 깊이 있는 지식을 보여줄 수 있는 좋은 기회야! 🧠
PHP의 동작 과정은 다음과 같아:
- 클라이언트(브라우저)가 웹 서버에 HTTP 요청을 보냄
- 웹 서버는 요청된 파일이 PHP 파일인지 확인하고, PHP 인터프리터에게 처리를 요청
- PHP 인터프리터는 PHP 코드를 실행하고, 필요시 데이터베이스와 통신
- PHP 인터프리터는 실행 결과를 HTML로 변환하여 웹 서버에 반환
- 웹 서버는 생성된 HTML을 클라이언트에게 HTTP 응답으로 전송
🔍 면접 예상 질문: "PHP는 어떻게 동작하나요? 인터프리터 언어와 컴파일 언어의 차이점은 무엇인가요?"
💡 답변 포인트:
PHP는 인터프리터 언어로, 코드를 한 줄씩 해석하고 실행해요. 하지만 최신 버전(PHP 8+)에서는 JIT(Just-In-Time) 컴파일러를 도입해 성능을 향상시켰습니다. 인터프리터 언어는 실행 시점에 코드를 해석하므로 개발 속도가 빠르고 디버깅이 쉽지만, 컴파일 언어보다 실행 속도가 느릴 수 있습니다. 컴파일 언어는 실행 전에 전체 코드를 기계어로 변환하므로 실행 속도가 빠르지만, 개발 과정에서 컴파일 단계가 필요합니다.
2. PHP 기본 문법 및 자료형 🧰
2.1 변수와 상수
PHP에서 변수는 $ 기호로 시작하고, 동적 타입 언어이기 때문에 타입을 명시적으로 선언할 필요가 없어. 하지만 PHP 7부터는 타입 힌팅을 사용할 수 있게 됐어! 👍
// 변수 선언 및 사용
$name = "홍길동";
$age = 30;
$isActive = true;
// PHP 7+ 타입 힌팅
function add(int $a, int $b): int {
return $a + $b;
}
// 상수 정의
define("PI", 3.14159);
const MAX_USERS = 100;
echo PI; // 출력: 3.14159
echo MAX_USERS; // 출력: 100
🔍 면접 예상 질문: "PHP에서 define과 const의 차이점은 무엇인가요?"
💡 답변 포인트:
- define()은 함수이므로 조건문 내에서 사용 가능하지만, const는 컴파일 타임에 정의되어야 함
- const는 클래스 내부에서 클래스 상수를 정의할 때 사용 가능
- define()으로 정의된 상수는 namespace의 영향을 받지 않음
- 성능 측면에서는 const가 약간 더 빠름
2.2 PHP 자료형
PHP는 다양한 자료형을 지원해. 기본 자료형부터 복합 자료형까지 알아두면 면접에서 유리해! 🤓
자료형 | 설명 | 예시 |
---|---|---|
Integer | 정수형 | $x = 42; |
Float | 실수형 | $x = 3.14; |
String | 문자열 | $x = "Hello"; |
Boolean | 논리값 | $x = true; |
Array | 배열 | $x = [1, 2, 3]; |
Object | 객체 | $x = new stdClass(); |
NULL | NULL 값 | $x = null; |
Resource | 외부 자원 | $file = fopen("test.txt", "r"); |
PHP 8부터는 Union Types이 도입되어 여러 타입을 함께 지정할 수 있게 됐어:
// PHP 8 Union Types
function processData(string|int $data): string|bool {
if (is_string($data)) {
return $data;
} else if (is_int($data)) {
return (string)$data;
}
return false;
}
2.3 배열(Array)
PHP의 배열은 매우 강력하고 유연해. 인덱스 배열, 연관 배열, 다차원 배열 등 다양한 형태로 사용할 수 있지! 🔄
// 인덱스 배열
$fruits = ["사과", "바나나", "오렌지"];
echo $fruits[0]; // 출력: 사과
// 연관 배열
$person = [
"name" => "홍길동",
"age" => 30,
"job" => "개발자"
];
echo $person["name"]; // 출력: 홍길동
// 다차원 배열
$employees = [
["name" => "김철수", "position" => "개발자"],
["name" => "이영희", "position" => "디자이너"],
["name" => "박지민", "position" => "PM"]
];
echo $employees[1]["name"]; // 출력: 이영희
배열 관련 주요 함수들도 알아두면 좋아:
- array_push(): 배열 끝에 요소 추가
- array_pop(): 배열 끝에서 요소 제거 및 반환
- array_shift(): 배열 앞에서 요소 제거 및 반환
- array_unshift(): 배열 앞에 요소 추가
- count(): 배열 요소 개수 반환
- array_merge(): 여러 배열 병합
- array_map(): 배열의 모든 요소에 함수 적용
- array_filter(): 조건에 맞는 요소만 필터링
- array_reduce(): 배열 요소를 하나의 값으로 줄임
- sort(), rsort(), asort(), ksort() 등: 다양한 정렬 함수
🔍 면접 예상 질문: "PHP에서 배열을 다루는 주요 함수들과 그 사용법에 대해 설명해주세요."
💡 답변 포인트:
PHP에서는 배열을 다루기 위한 다양한 내장 함수를 제공합니다. 예를 들어, array_map()은 배열의 모든 요소에 콜백 함수를 적용하여 새 배열을 반환하고, array_filter()는 콜백 함수를 통과하는 요소만 포함한 새 배열을 반환합니다. array_reduce()는 배열의 모든 요소를 단일 값으로 줄이는데 사용됩니다. 이런 함수들을 활용하면 함수형 프로그래밍 스타일로 배열을 효율적으로 처리할 수 있습니다.
3. PHP 객체지향 프로그래밍(OOP) 💎
3.1 클래스와 객체
PHP 5부터 본격적으로 객체지향 프로그래밍을 지원하기 시작했어. 면접에서 OOP 개념은 거의 필수로 물어보니 확실히 알아두자! 🧠
// 기본 클래스 정의
class User {
// 속성(Properties)
public $name;
private $email;
protected $role;
// 생성자
public function __construct(string $name, string $email, string $role = 'user') {
$this->name = $name;
$this->email = $email;
$this->role = $role;
}
// 메서드(Methods)
public function getEmail() {
return $this->email;
}
public function setEmail(string $email) {
$this->email = $email;
}
public function getRole() {
return $this->role;
}
}
// 객체 생성 및 사용
$user = new User("홍길동", "hong@example.com");
echo $user->name; // 출력: 홍길동
echo $user->getEmail(); // 출력: hong@example.com
3.2 상속과 다형성
객체지향의 핵심 개념인 상속과 다형성도 PHP에서 잘 지원해. 이 개념들을 이해하고 있으면 면접에서 좋은 인상을 줄 수 있어! 👌
// 상속 예제
class Admin extends User {
private $adminLevel;
public function __construct(string $name, string $email, int $adminLevel = 1) {
// 부모 클래스 생성자 호출
parent::__construct($name, $email, 'admin');
$this->adminLevel = $adminLevel;
}
public function getAdminLevel() {
return $this->adminLevel;
}
// 메서드 오버라이딩
public function getRole() {
return "관리자(레벨 {$this->adminLevel})";
}
}
$admin = new Admin("관리자", "admin@example.com", 2);
echo $admin->getRole(); // 출력: 관리자(레벨 2)
3.3 추상 클래스와 인터페이스
추상 클래스와 인터페이스는 코드의 구조화와 설계에 중요한 역할을 해. 이 개념들도 면접에서 자주 물어보니 잘 알아두자! 🏗️
// 추상 클래스
abstract class Vehicle {
protected $brand;
public function __construct(string $brand) {
$this->brand = $brand;
}
// 추상 메서드 - 자식 클래스에서 반드시 구현해야 함
abstract public function start();
// 일반 메서드
public function getBrand() {
return $this->brand;
}
}
// 인터페이스
interface Chargeable {
public function charge();
}
// 추상 클래스 상속 및 인터페이스 구현
class ElectricCar extends Vehicle implements Chargeable {
private $batteryLevel;
public function __construct(string $brand, int $batteryLevel = 100) {
parent::__construct($brand);
$this->batteryLevel = $batteryLevel;
}
// 추상 메서드 구현
public function start() {
return "{$this->brand} 전기차가 조용히 시동됩니다.";
}
// 인터페이스 메서드 구현
public function charge() {
$this->batteryLevel = 100;
return "배터리가 충전되었습니다.";
}
public function getBatteryLevel() {
return $this->batteryLevel;
}
}
$tesla = new ElectricCar("Tesla");
echo $tesla->start(); // 출력: Tesla 전기차가 조용히 시동됩니다.
echo $tesla->charge(); // 출력: 배터리가 충전되었습니다.
3.4 트레이트(Trait)
PHP는 다중 상속을 지원하지 않지만, 트레이트를 통해 코드 재사용성을 높일 수 있어. 이것도 면접에서 물어볼 수 있는 중요한 개념이야! 🔄
// 트레이트 정의
trait Loggable {
public function log($message) {
echo "[로그] {$message}\n";
}
}
trait Serializable {
public function serialize() {
return serialize($this);
}
public function unserialize($data) {
return unserialize($data);
}
}
// 클래스에서 트레이트 사용
class Product {
use Loggable, Serializable;
private $name;
private $price;
public function __construct(string $name, float $price) {
$this->name = $name;
$this->price = $price;
$this->log("새 상품 '{$name}'이 생성되었습니다.");
}
}
$product = new Product("노트북", 1200000);
// 출력: [로그] 새 상품 '노트북'이 생성되었습니다.
$serialized = $product->serialize();
echo $serialized; // 직렬화된 객체 출력
🔍 면접 예상 질문: "PHP에서 트레이트(Trait)란 무엇이며, 어떤 상황에서 사용하나요? 상속과의 차이점은 무엇인가요?"
💡 답변 포인트:
트레이트는 PHP에서 코드 재사용을 위한 메커니즘으로, 다중 상속의 한계를 극복하기 위해 도입되었습니다. 클래스는 여러 트레이트를 동시에 사용할 수 있어 코드 중복을 줄이고 기능을 모듈화할 수 있습니다. 상속과 달리 트레이트는 'is-a' 관계가 아닌 'has-a' 관계를 표현하며, 여러 클래스에서 동일한 메서드를 재사용하고 싶을 때 유용합니다. 특히 PHP가 단일 상속만 지원하는 상황에서 여러 기능을 조합해야 할 때 트레이트가 효과적입니다.
3.5 정적 메서드와 속성
정적(static) 메서드와 속성은 객체를 생성하지 않고도 클래스에서 직접 접근할 수 있어. 유틸리티 함수나 공유 데이터에 유용하지! 🔧
class MathUtils {
// 정적 속성
public static $pi = 3.14159;
// 정적 메서드
public static function square($number) {
return $number * $number;
}
public static function cube($number) {
return $number * $number * $number;
}
}
// 정적 메서드와 속성 사용
echo MathUtils::$pi; // 출력: 3.14159
echo MathUtils::square(4); // 출력: 16
echo MathUtils::cube(3); // 출력: 27
3.6 매직 메서드
PHP 클래스에는 특별한 동작을 정의할 수 있는 매직 메서드들이 있어. 이름이 __ (언더스코어 두 개)로 시작하는 이 메서드들은 특정 상황에서 자동으로 호출돼! 🪄
class Person {
private $data = [];
// 존재하지 않는 속성에 접근할 때 호출
public function __get($name) {
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
return null;
}
// 존재하지 않는 속성에 값을 설정할 때 호출
public function __set($name, $value) {
$this->data[$name] = $value;
}
// isset() 함수가 호출될 때
public function __isset($name) {
return isset($this->data[$name]);
}
// unset() 함수가 호출될 때
public function __unset($name) {
unset($this->data[$name]);
}
// 객체가 문자열로 변환될 때 호출
public function __toString() {
return json_encode($this->data);
}
// 객체가 함수처럼 호출될 때
public function __invoke($param) {
return "객체가 함수로 호출됨: {$param}";
}
}
$person = new Person();
$person->name = "홍길동"; // __set() 호출
echo $person->name; // __get() 호출, 출력: 홍길동
echo isset($person->age); // __isset() 호출, 출력: false
$person->age = 30;
echo isset($person->age); // __isset() 호출, 출력: true
unset($person->age); // __unset() 호출
echo $person; // __toString() 호출, 출력: {"name":"홍길동"}
echo $person("테스트"); // __invoke() 호출, 출력: 객체가 함수로 호출됨: 테스트
🔍 면접 예상 질문: "PHP의 주요 매직 메서드들과 그 용도에 대해 설명해주세요."
💡 답변 포인트:
- __construct(): 객체 생성 시 호출되는 생성자
- __destruct(): 객체가 소멸될 때 호출되는 소멸자
- __get(), __set(): 접근할 수 없는 속성을 읽거나 쓸 때 호출
- __call(), __callStatic(): 접근할 수 없는 메서드를 호출할 때 사용
- __toString(): 객체를 문자열로 변환할 때 호출
- __clone(): 객체가 복제될 때 호출
- __invoke(): 객체를 함수처럼 호출할 때 사용
- __sleep(), __wakeup(): 직렬화, 역직렬화 시 호출
4. PHP 웹 개발 핵심 개념 🌐
4.1 슈퍼글로벌 변수
PHP에는 어디서든 접근 가능한 슈퍼글로벌 변수들이 있어. 웹 개발에서 정말 중요한 개념이니 꼭 알아두자! 🌍
변수 | 설명 | 예시 |
---|---|---|
$_GET | URL 매개변수(쿼리 스트링)를 통해 전달된 데이터 | $_GET['id'] |
$_POST | HTTP POST 메서드로 전송된 데이터 | $_POST['username'] |
$_REQUEST | $_GET, $_POST, $_COOKIE를 포함 | $_REQUEST['data'] |
$_SESSION | 세션 변수 | $_SESSION['user_id'] |
$_COOKIE | 쿠키 변수 | $_COOKIE['preference'] |
$_FILES | 업로드된 파일 정보 | $_FILES['upload']['name'] |
$_SERVER | 서버 및 실행 환경 정보 | $_SERVER['REQUEST_METHOD'] |
$_ENV | 환경 변수 | $_ENV['HOME'] |
$GLOBALS | 모든 전역 변수에 대한 참조 | $GLOBALS['variable'] |
🔍 면접 예상 질문: "$_GET과 $_POST의 차이점은 무엇이며, 각각 어떤 상황에서 사용하는 것이 적합한가요?"
💡 답변 포인트:
$_GET은 URL에 데이터가 노출되어 보안에 취약하지만, 북마크가 가능하고 브라우저 히스토리에 저장됩니다. 주로 검색이나 필터링 같은 데이터 조회 작업에 적합합니다. $_POST는 HTTP 요청 본문에 데이터를 포함시켜 URL에 노출되지 않으며, 더 많은 데이터를 전송할 수 있습니다. 로그인, 회원가입, 데이터 추가/수정 등 서버의 상태를 변경하는 작업에 적합합니다. 보안이 중요하거나 대용량 데이터 전송이 필요한 경우 $_POST를 사용하는 것이 좋습니다.
4.2 세션과 쿠키
세션과 쿠키는 웹 애플리케이션에서 상태를 유지하기 위한 중요한 메커니즘이야. 면접에서 자주 물어보는 주제니 확실히 이해하고 가자! 🍪
// 세션 시작
session_start();
// 세션 변수 설정
$_SESSION['user_id'] = 123;
$_SESSION['username'] = '홍길동';
// 세션 변수 사용
echo $_SESSION['username']; // 출력: 홍길동
// 특정 세션 변수 삭제
unset($_SESSION['username']);
// 모든 세션 변수 삭제
$_SESSION = array();
// 세션 쿠키 삭제
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
// 세션 파괴
session_destroy();
// 쿠키 설정 (이름, 값, 만료 시간, 경로, 도메인, 보안, HTTP 전용)
setcookie("user_preference", "dark_mode", time() + 86400 * 30, "/", "", false, true);
// 쿠키 읽기
if (isset($_COOKIE['user_preference'])) {
echo $_COOKIE['user_preference']; // 출력: dark_mode
}
// 쿠키 삭제 (만료 시간을 과거로 설정)
setcookie("user_preference", "", time() - 3600);
세션과 쿠키의 주요 차이점:
특성 | 세션 | 쿠키 |
---|---|---|
저장 위치 | 서버 | 클라이언트(브라우저) |
보안 | 상대적으로 안전 | 클라이언트에서 수정 가능 |
수명 | 브라우저 종료 시 만료(기본값) | 설정한 만료 시간까지 |
크기 제한 | 서버 설정에 따라 다름 | 브라우저당 약 4KB |
사용 사례 | 로그인 정보, 장바구니 등 | 사용자 환경설정, 추적 등 |
4.3 폼 처리와 데이터 검증
웹 애플리케이션에서 폼 데이터를 안전하게 처리하고 검증하는 것은 매우 중요해. 보안과 직결되는 부분이니 잘 알아두자! 📝
// 폼 제출 처리
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// 데이터 검증 및 정제
$name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING);
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, [
'options' => ['min_range' => 1, 'max_range' => 120]
]);
// 오류 처리
$errors = [];
if (!$name || empty($name)) {
$errors[] = "이름이 유효하지 않습니다.";
}
if (!$email) {
$errors[] = "이메일이 유효하지 않습니다.";
}
if ($age === false) {
$errors[] = "나이가 유효하지 않습니다.";
}
// 오류가 없으면 처리 진행
if (empty($errors)) {
// 데이터베이스 저장 등의 처리
echo "폼이 성공적으로 제출되었습니다!";
} else {
// 오류 메시지 표시
foreach ($errors as $error) {
echo $error . "
";
}
}
}
데이터 검증 시 주의할 점:
- XSS(Cross-Site Scripting) 방지: 사용자 입력을 출력할 때 htmlspecialchars() 함수 사용
- SQL 인젝션 방지: 준비된 구문(Prepared Statements) 사용
- CSRF(Cross-Site Request Forgery) 방지: CSRF 토큰 사용
- 적절한 데이터 타입 검증: filter_var() 또는 filter_input() 함수 활용
- 서버 측 검증: 클라이언트 측 검증과 별개로 항상 서버에서도 검증 수행
🔍 면접 예상 질문: "PHP에서 사용자 입력을 안전하게 처리하는 방법에 대해 설명해주세요."
💡 답변 포인트:
PHP에서는 사용자 입력을 안전하게 처리하기 위해 여러 방법을 사용합니다. 먼저, filter_input() 함수와 다양한 필터를 활용하여 입력 데이터를 검증하고 정제할 수 있습니다. XSS 공격을 방지하기 위해 htmlspecialchars() 함수를 사용하여 출력 시 특수 문자를 이스케이프 처리합니다. SQL 인젝션을 방지하기 위해서는 PDO나 mysqli의 prepared statements를 사용합니다. 또한, CSRF 공격을 방지하기 위해 폼에 토큰을 포함시키고 서버에서 검증합니다. 마지막으로, 클라이언트 측 검증은 편의를 위한 것이고, 항상 서버 측에서 철저한 검증을 수행해야 합니다.
4.4 파일 업로드 처리
파일 업로드는 웹 애플리케이션에서 자주 사용되는 기능이지만, 보안 위험이 있어. 안전하게 처리하는 방법을 알아보자! 📂
// 파일 업로드 처리
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_FILES["fileToUpload"])) {
$targetDir = "uploads/";
$targetFile = $targetDir . basename($_FILES["fileToUpload"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($targetFile, PATHINFO_EXTENSION));
// 파일이 실제 이미지인지 확인
$check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
if ($check === false) {
echo "파일이 이미지가 아닙니다.";
$uploadOk = 0;
}
// 파일이 이미 존재하는지 확인
if (file_exists($targetFile)) {
echo "같은 이름의 파일이 이미 존재합니다.";
$uploadOk = 0;
}
// 파일 크기 제한 (5MB)
if ($_FILES["fileToUpload"]["size"] > 5000000) {
echo "파일이 너무 큽니다.";
$uploadOk = 0;
}
// 특정 파일 형식만 허용
if (!in_array($imageFileType, ["jpg", "png", "jpeg", "gif"])) {
echo "JPG, JPEG, PNG, GIF 파일만 업로드 가능합니다.";
$uploadOk = 0;
}
// 모든 검사를 통과했는지 확인
if ($uploadOk == 0) {
echo "파일이 업로드되지 않았습니다.";
} else {
// 파일 업로드 시도
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $targetFile)) {
echo "파일 " . basename($_FILES["fileToUpload"]["name"]) . "이(가) 업로드되었습니다.";
} else {
echo "파일 업로드 중 오류가 발생했습니다.";
}
}
}
파일 업로드 보안 체크리스트:
- 파일 형식 검증 (확장자, MIME 타입)
- 파일 크기 제한
- 파일명 변경 (예: 랜덤 이름 + 타임스탬프)
- 업로드 디렉토리 권한 설정
- 업로드된 파일 실행 방지 (.htaccess 설정)
- 파일 내용 검사 (가능한 경우)
5. PHP 데이터베이스 연동 💾
5.1 PDO(PHP Data Objects)
PDO는 여러 데이터베이스에 일관된 방식으로 접근할 수 있게 해주는 데이터베이스 추상화 레이어야. 보안과 이식성 측면에서 권장되는 방식이니 꼭 알아두자! 🔒
try {
// PDO 인스턴스 생성
$pdo = new PDO(
'mysql:host=localhost;dbname=testdb;charset=utf8mb4',
'username',
'password',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]
);
// 준비된 구문(Prepared Statement) 사용
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email AND status = :status");
// 매개변수 바인딩
$stmt->bindParam(':email', $email, PDO::PARAM_STR);
$stmt->bindParam(':status', $status, PDO::PARAM_INT);
// 값 설정 및 실행
$email = 'user@example.com';
$status = 1;
$stmt->execute();
// 결과 가져오기
$users = $stmt->fetchAll();
// 결과 처리
foreach ($users as $user) {
echo $user['name'] . '
';
}
// 다른 방식의 매개변수 전달
$stmt = $pdo->prepare("INSERT INTO users (name, email, created_at) VALUES (?, ?, ?)");
$stmt->execute(['홍길동', 'hong@example.com', date('Y-m-d H:i:s')]);
// 마지막 삽입 ID 가져오기
$lastId = $pdo->lastInsertId();
echo "마지막 삽입 ID: " . $lastId;
} catch (PDOException $e) {
// 오류 처리
echo "데이터베이스 오류: " . $e->getMessage();
}
5.2 MySQLi
MySQLi(MySQL Improved)는 MySQL 데이터베이스와 작업하기 위한 확장 기능이야. PDO와 함께 많이 사용되는 방식이지! 🔄
// 객체 지향 방식
$mysqli = new mysqli("localhost", "username", "password", "database");
// 연결 확인
if ($mysqli->connect_errno) {
echo "MySQL 연결 실패: " . $mysqli->connect_error;
exit();
}
// 준비된 구문
$stmt = $mysqli->prepare("SELECT id, name FROM users WHERE email = ?");
$email = "user@example.com";
$stmt->bind_param("s", $email); // s는 string 타입을 의미
$stmt->execute();
// 결과 바인딩
$stmt->bind_result($id, $name);
// 결과 가져오기
while ($stmt->fetch()) {
echo "ID: " . $id . ", 이름: " . $name . "
";
}
// 구문 닫기
$stmt->close();
// 데이터 삽입
$stmt = $mysqli->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$name = "김철수";
$email = "kim@example.com";
$stmt->bind_param("ss", $name, $email);
$stmt->execute();
echo "영향 받은 행: " . $stmt->affected_rows;
$stmt->close();
// 연결 닫기
$mysqli->close();
5.3 PDO vs MySQLi
PDO와 MySQLi 중 어떤 것을 선택해야 할지 면접에서 물어볼 수 있어. 둘의 차이점을 알아두자! 🤔
특성 | PDO | MySQLi |
---|---|---|
데이터베이스 지원 | 12개 이상의 다양한 데이터베이스 | MySQL/MariaDB만 지원 |
API | 객체 지향적 인터페이스만 제공 | 객체 지향 및 절차적 인터페이스 모두 제공 |
명명된 매개변수 | 지원 (:name) | 지원하지 않음 (위치 기반 ?) |
예외 처리 | 기본적으로 예외 사용 | 오류 코드/메시지 사용 (예외는 옵션) |
성능 | 약간 느림 | 약간 빠름 |
🔍 면접 예상 질문: "PDO와 MySQLi 중 어떤 것을 선호하며, 그 이유는 무엇인가요?"
💡 답변 포인트:
저는 일반적으로 PDO를 선호합니다. 그 이유는 다음과 같습니다:
- 데이터베이스 추상화: 프로젝트가 다른 데이터베이스로 마이그레이션해야 할 경우 코드 변경이 최소화됩니다.
- 명명된 매개변수: 위치 기반 매개변수보다 가독성이 좋고 유지보수가 쉽습니다.
- 일관된 오류 처리: PDO는 예외를 통한 오류 처리 방식을 사용하여 코드가 더 깔끔해집니다.
- 보안: PDO와 MySQLi 모두 준비된 구문을 통해 SQL 인젝션을 방지할 수 있지만, PDO의 인터페이스가 더 사용하기 쉽습니다.
다만, MySQL의 고급 기능을 많이 사용하거나 성능이 매우 중요한 경우에는 MySQLi가 더 적합할 수 있습니다.
5.4 트랜잭션 처리
트랜잭션은 데이터베이스의 무결성을 유지하기 위한 중요한 개념이야. 여러 쿼리가 하나의 작업 단위로 처리되어야 할 때 사용해! 🔄
// PDO에서 트랜잭션 사용
try {
$pdo->beginTransaction();
// 첫 번째 쿼리
$stmt1 = $pdo->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?");
$stmt1->execute([100, 1]);
// 두 번째 쿼리
$stmt2 = $pdo->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?");
$stmt2->execute([100, 2]);
// 모든 쿼리가 성공하면 커밋
$pdo->commit();
echo "트랜잭션이 성공적으로 완료되었습니다.";
} catch (Exception $e) {
// 오류 발생 시 롤백
$pdo->rollBack();
echo "트랜잭션 실패: " . $e->getMessage();
}
// MySQLi에서 트랜잭션 사용
$mysqli->autocommit(FALSE); // 자동 커밋 비활성화
$query1 = "UPDATE accounts SET balance = balance - 100 WHERE id = 1";
$query2 = "UPDATE accounts SET balance = balance + 100 WHERE id = 2";
$result1 = $mysqli->query($query1);
$result2 = $mysqli->query($query2);
if ($result1 && $result2) {
$mysqli->commit();
echo "트랜잭션이 성공적으로 완료되었습니다.";
} else {
$mysqli->rollback();
echo "트랜잭션 실패";
}
$mysqli->autocommit(TRUE); // 자동 커밋 다시 활성화
6. PHP 보안 🔒
6.1 SQL 인젝션 방지
SQL 인젝션은 가장 흔한 웹 애플리케이션 공격 중 하나야. 이를 방지하는 방법을 확실히 알아두는 것이 중요해! 🛡️
// 잘못된 방법 (SQL 인젝션에 취약)
$username = $_POST['username'];
$query = "SELECT * FROM users WHERE username = '$username'";
$result = $mysqli->query($query); // 위험!
// 올바른 방법 (PDO 사용)
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->bindParam(':username', $_POST['username'], PDO::PARAM_STR);
$stmt->execute();
$user = $stmt->fetch();
// 올바른 방법 (MySQLi 사용)
$stmt = $mysqli->prepare("SELECT * FROM users WHERE username = ?");
$stmt->bind_param("s", $_POST['username']);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
6.2 XSS(Cross-Site Scripting) 방지
XSS는 악성 스크립트를 웹 페이지에 삽입하는 공격이야. 사용자 입력을 출력할 때 항상 이스케이프 처리를 해야 해! 🔍
// 잘못된 방법 (XSS에 취약)
echo "안녕하세요, " . $_GET['name'] . "님!"; // 위험!
// 올바른 방법
echo "안녕하세요, " . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8') . "님!";
// 함수로 만들어 사용
function h($string) {
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
echo "안녕하세요, " . h($_GET['name']) . "님!";
6.3 CSRF(Cross-Site Request Forgery) 방지
CSRF는 사용자가 의도하지 않은 요청을 서버에 보내도록 속이는 공격이야. 토큰을 사용해 방지할 수 있어! 🔑
각 프레임워크별 특징을 좀 더 자세히 알아보자:
Laravel
Laravel은 현재 가장 인기 있는 PHP 프레임워크로, 우아한 문법과 다양한 기능을 제공해. MVC 아키텍처, Eloquent ORM, Blade 템플릿 엔진, Artisan CLI 등 개발자 경험을 향상시키는 다양한 기능을 갖추고 있지! 🌟
Symfony
Symfony는 재사용 가능한 PHP 컴포넌트와 라이브러리의 집합으로, 대규모 엔터프라이즈 애플리케이션에 적합해. 높은 유연성과 확장성을 제공하며, 다른 프레임워크(Laravel 포함)의 기반이 되기도 해! 🏢
CodeIgniter
CodeIgniter는 가볍고 성능이 좋은 프레임워크로, 학습 곡선이 낮아 초보자에게 적합해. 최소한의 설정으로 빠르게 개발을 시작할 수 있고, 문서화가 잘 되어 있어! 🚀
CakePHP
CakePHP는 코드 생성 도구와 스캐폴딩 기능이 강점인 프레임워크야. 규약을 통한 설정(Convention over Configuration) 원칙을 따르며, 빠른 개발이 가능해! 🍰
Yii
Yii는 고성능 컴포넌트 기반 프레임워크로, 특히 대규모 웹 애플리케이션 개발에 적합해. ActiveRecord ORM, 캐싱 지원 등 성능 최적화 기능이 강점이야! ⚡
Slim
Slim은 경량 마이크로 프레임워크로, RESTful API 개발에 특화되어 있어. 필요한 기능만 포함하고 있어 가볍고 빠르며, 미들웨어를 통한 확장이 용이해! 🔄
🔍 면접 예상 질문: "Laravel과 Symfony의 주요 차이점은 무엇이며, 어떤 상황에서 각각을 선택하겠습니까?"
💡 답변 포인트:
Laravel은 개발자 경험과 생산성에 중점을 두며, 우아한 문법과 다양한 내장 기능을 제공합니다. 중소규모 프로젝트나 빠른 개발이 필요한 경우, 또는 팀이 Laravel 경험이 있을 때 적합합니다.
Symfony는 모듈화된 컴포넌트 기반 구조로, 높은 유연성과 확장성을 제공합니다. 대규모 엔터프라이즈 애플리케이션, 장기적인 유지보수가 필요한 프로젝트, 또는 특정 요구사항에 맞게 프레임워크를 커스터마이징해야 할 때 적합합니다.
프로젝트의 규모, 복잡성, 팀의 경험, 개발 일정 등을 고려하여 선택하는 것이 중요합니다. Laravel은 빠른 개발과 쉬운 학습 곡선이 필요할 때, Symfony는 높은 유연성과 엔터프라이즈급 안정성이 필요할 때 선택하겠습니다.
8.2 주요 PHP 라이브러리 및 패키지
PHP 생태계에는 다양한 라이브러리와 패키지가 있어. Composer를 통해 쉽게 설치하고 사용할 수 있지! 📦
라이브러리 | 용도 | 특징 |
---|---|---|
Guzzle | HTTP 클라이언트 | RESTful API 호출, 비동기 요청 지원 |
Carbon | 날짜 및 시간 처리 | 직관적인 API, 날짜 연산 및 형식화 |
PHPUnit | 단위 테스트 | 자동화된 테스트, 목(Mock) 객체 지원 |
Monolog | 로깅 | 다양한 로그 핸들러, PSR-3 호환 |
Doctrine | ORM | 객체 관계 매핑, 데이터베이스 추상화 |
Twig | 템플릿 엔진 | 유연한 문법, 상속 및 확장 지원 |
SwiftMailer | 이메일 전송 | SMTP, 첨부 파일, HTML 이메일 지원 |
PHP-DI | 의존성 주입 | 자동 와이어링, 어노테이션 지원 |
// Composer를 사용한 패키지 설치
// composer.json 파일
{
"require": {
"guzzlehttp/guzzle": "^7.0",
"nesbot/carbon": "^2.0",
"monolog/monolog": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
}
}
// 설치 명령어
// $ composer install
// Guzzle 사용 예시
use GuzzleHttp\Client;
$client = new Client();
$response = $client->request('GET', 'https://api.example.com/users');
$data = json_decode($response->getBody(), true);
// Carbon 사용 예시
use Carbon\Carbon;
$now = Carbon::now();
echo $now->format('Y-m-d'); // 2025-02-28
echo $now->addDays(7)->format('Y-m-d'); // 2025-03-07
echo Carbon::parse('2025-01-01')->diffForHumans(); // 1 month ago
// Monolog 사용 예시
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger('app');
$log->pushHandler(new StreamHandler('logs/app.log', Logger::WARNING));
$log->warning('This is a warning message');
$log->error('This is an error message');
9. PHP 디자인 패턴 🧠
9.1 싱글톤 패턴(Singleton Pattern)
싱글톤 패턴은 클래스의 인스턴스가 하나만 생성되도록 보장하는 패턴이야. 데이터베이스 연결, 로깅 등에 자주 사용돼! 🔄
class Database {
private static $instance = null;
private $connection;
// 생성자를 private으로 선언하여 외부에서 인스턴스 생성 방지
private function __construct() {
$this->connection = new PDO('mysql:host=localhost;dbname=testdb', 'username', 'password');
}
// 싱글톤 인스턴스 가져오기
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getConnection() {
return $this->connection;
}
// 복제 방지
private function __clone() {}
// 직렬화 방지
private function __wakeup() {}
}
// 사용 예시
$db1 = Database::getInstance();
$db2 = Database::getInstance();
// $db1과 $db2는 동일한 인스턴스
var_dump($db1 === $db2); // bool(true)
9.2 팩토리 패턴(Factory Pattern)
팩토리 패턴은 객체 생성 로직을 캡슐화하여 클라이언트 코드와 분리하는 패턴이야. 객체 생성의 유연성을 높여줘! 🏭
// 제품 인터페이스
interface PaymentGateway {
public function processPayment($amount);
}
// 구체적인 제품 클래스들
class PayPalGateway implements PaymentGateway {
public function processPayment($amount) {
return "PayPal 결제: {$amount}원";
}
}
class StripeGateway implements PaymentGateway {
public function processPayment($amount) {
return "Stripe 결제: {$amount}원";
}
}
class KakaoPayGateway implements PaymentGateway {
public function processPayment($amount) {
return "KakaoPay 결제: {$amount}원";
}
}
// 팩토리 클래스
class PaymentGatewayFactory {
public static function create($type) {
switch ($type) {
case 'paypal':
return new PayPalGateway();
case 'stripe':
return new StripeGateway();
case 'kakaopay':
return new KakaoPayGateway();
default:
throw new Exception("지원하지 않는 결제 방식입니다: {$type}");
}
}
}
// 사용 예시
$gateway = PaymentGatewayFactory::create('paypal');
echo $gateway->processPayment(10000); // PayPal 결제: 10000원
$gateway = PaymentGatewayFactory::create('stripe');
echo $gateway->processPayment(20000); // Stripe 결제: 20000원
9.3 전략 패턴(Strategy Pattern)
전략 패턴은 알고리즘을 캡슐화하고 런타임에 교체 가능하게 만드는 패턴이야. 다양한 동작을 유연하게 처리할 수 있어! 🎯
// 전략 인터페이스
interface SortStrategy {
public function sort(array $data): array;
}
// 구체적인 전략 클래스들
class BubbleSortStrategy implements SortStrategy {
public function sort(array $data): array {
echo "버블 정렬 사용\n";
// 버블 정렬 구현
$n = count($data);
for ($i = 0; $i < $n; $i++) {
for ($j = 0; $j < $n - $i - 1; $j++) {
if ($data[$j] > $data[$j + 1]) {
$temp = $data[$j];
$data[$j] = $data[$j + 1];
$data[$j + 1] = $temp;
}
}
}
return $data;
}
}
class QuickSortStrategy implements SortStrategy {
public function sort(array $data): array {
echo "퀵 정렬 사용\n";
// 퀵 정렬 구현
if (count($data) <= 1) {
return $data;
}
$pivot = $data[0];
$left = $right = [];
for ($i = 1; $i < count($data); $i++) {
if ($data[$i] < $pivot) {
$left[] = $data[$i];
} else {
$right[] = $data[$i];
}
}
return array_merge(
$this->sort($left),
[$pivot],
$this->sort($right)
);
}
}
// 컨텍스트 클래스
class Sorter {
private $strategy;
public function __construct(SortStrategy $strategy) {
$this->strategy = $strategy;
}
public function setStrategy(SortStrategy $strategy) {
$this->strategy = $strategy;
}
public function sort(array $data): array {
return $this->strategy->sort($data);
}
}
// 사용 예시
$data = [5, 3, 9, 1, 7];
// 작은 데이터셋에는 버블 정렬 사용
$sorter = new Sorter(new BubbleSortStrategy());
$sorted = $sorter->sort($data);
print_r($sorted);
// 큰 데이터셋에는 퀵 정렬로 전략 변경
$sorter->setStrategy(new QuickSortStrategy());
$sorted = $sorter->sort($data);
print_r($sorted);
9.4 옵저버 패턴(Observer Pattern)
옵저버 패턴은 객체 간의 일대다 의존 관계를 정의하여 한 객체의 상태가 변경되면 의존 객체들에게 자동으로 알림이 가는 패턴이야. 이벤트 처리에 유용해! 👀
// 주제(Subject) 인터페이스
interface Subject {
public function attach(Observer $observer);
public function detach(Observer $observer);
public function notify();
}
// 옵저버 인터페이스
interface Observer {
public function update(Subject $subject);
}
// 구체적인 주제 클래스
class Newsletter implements Subject {
private $observers = [];
private $content;
public function attach(Observer $observer) {
$this->observers[] = $observer;
}
public function detach(Observer $observer) {
$key = array_search($observer, $this->observers, true);
if ($key !== false) {
unset($this->observers[$key]);
}
}
public function notify() {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
public function setContent($content) {
$this->content = $content;
$this->notify();
}
public function getContent() {
return $this->content;
}
}
// 구체적인 옵저버 클래스들
class EmailSubscriber implements Observer {
private $email;
public function __construct($email) {
$this->email = $email;
}
public function update(Subject $subject) {
echo "이메일 발송: {$this->email} - 새 뉴스레터: {$subject->getContent()}\n";
}
}
class SMSSubscriber implements Observer {
private $phone;
public function __construct($phone) {
$this->phone = $phone;
}
public function update(Subject $subject) {
echo "SMS 발송: {$this->phone} - 새 뉴스레터: {$subject->getContent()}\n";
}
}
// 사용 예시
$newsletter = new Newsletter();
$emailSubscriber1 = new EmailSubscriber("user1@example.com");
$emailSubscriber2 = new EmailSubscriber("user2@example.com");
$smsSubscriber = new SMSSubscriber("010-1234-5678");
$newsletter->attach($emailSubscriber1);
$newsletter->attach($emailSubscriber2);
$newsletter->attach($smsSubscriber);
$newsletter->setContent("2025년 3월 뉴스레터가 발행되었습니다!");
// 구독 해지
$newsletter->detach($emailSubscriber2);
$newsletter->setContent("특별 할인 이벤트 안내!");
🔍 면접 예상 질문: "PHP에서 자주 사용되는 디자인 패턴에 대해 설명하고, 실제 프로젝트에서 어떻게 활용했는지 예를 들어주세요."
💡 답변 포인트:
PHP에서 자주 사용되는 디자인 패턴으로는 싱글톤, 팩토리, 전략, 옵저버, 의존성 주입 패턴 등이 있습니다. 예를 들어, 저는 최근 프로젝트에서 결제 시스템을 구현할 때 전략 패턴을 활용했습니다. 다양한 결제 방식(신용카드, 계좌이체, 페이팔 등)을 각각의 전략 클래스로 구현하고, 클라이언트가 선택한 결제 방식에 따라 적절한 전략을 주입하여 사용했습니다. 이를 통해 새로운 결제 방식이 추가되더라도 기존 코드를 수정하지 않고 확장할 수 있었고, 각 결제 방식의 로직을 독립적으로 관리할 수 있었습니다. 또한 데이터베이스 연결을 관리하기 위해 싱글톤 패턴을 사용하여 애플리케이션 전체에서 하나의 연결 인스턴스만 사용하도록 했습니다.
- 지식인의 숲 - 지적 재산권 보호 고지
지적 재산권 보호 고지
- 저작권 및 소유권: 본 컨텐츠는 재능넷의 독점 AI 기술로 생성되었으며, 대한민국 저작권법 및 국제 저작권 협약에 의해 보호됩니다.
- AI 생성 컨텐츠의 법적 지위: 본 AI 생성 컨텐츠는 재능넷의 지적 창작물로 인정되며, 관련 법규에 따라 저작권 보호를 받습니다.
- 사용 제한: 재능넷의 명시적 서면 동의 없이 본 컨텐츠를 복제, 수정, 배포, 또는 상업적으로 활용하는 행위는 엄격히 금지됩니다.
- 데이터 수집 금지: 본 컨텐츠에 대한 무단 스크래핑, 크롤링, 및 자동화된 데이터 수집은 법적 제재의 대상이 됩니다.
- AI 학습 제한: 재능넷의 AI 생성 컨텐츠를 타 AI 모델 학습에 무단 사용하는 행위는 금지되며, 이는 지적 재산권 침해로 간주됩니다.
재능넷은 최신 AI 기술과 법률에 기반하여 자사의 지적 재산권을 적극적으로 보호하며,
무단 사용 및 침해 행위에 대해 법적 대응을 할 권리를 보유합니다.
© 2025 재능넷 | All rights reserved.
댓글 0개