간단하게 이메일을 데이터베이스에 저장하는 API를 만들어보겠다..!
먼저 테이블을 다음과 같이 만들어준다.
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(100) NOT NULL,
createdat DATETIME NOT NULL
이렇게 3개만 넣자.
코드 먼저 살펴보자.
다음의 API는 이메일이 있는지 먼저 확인하고 있다면 409 오류 응답코드를 반환하고
없다면 INSERT 하는 것이다.
<?php
header('content-type:application/json;charset=utf-8');
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type");
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
$action = $_GET['action'] ?? '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'reserveUser') {
$inputData = file_get_contents('php://input');
$data = json_decode($inputData, true);
$email = $data['email'] ?? '';
if (!empty($email)) {
get_reserved($email);
} else {
http_response_code(400);
echo json_encode(['message' => 'Email is required']);
}
} else {
http_response_code(404);
echo json_encode(['message' => 'API not found']);
}
function get_reserved($email) {
try {
$pdo = new PDO('mysql:host=비밀;dbname=비밀', '비밀', '비밀');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$preQuery = "
SELECT email FROM reserved_user WHERE email = :email
";
$stmt = $pdo->prepare($preQuery);
$stmt->bindParam(':email', $email);
$stmt->execute();
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($result)) {
http_response_code(409);
echo json_encode(['message' => 'Email is already reserved']);
} else {
$query = "
INSERT INTO reserved_user
(email, createdat)
VALUES
(:email, CURRENT_TIME());
";
$preparedstmt = $pdo->prepare($query);
$preparedstmt->bindParam(':email', $email);
$preparedstmt->execute();
http_response_code(201);
echo json_encode(['message' => 'success']);
}
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['message' => 'Database error: ' . $e->getMessage()]);
}
}
?>
그럼 기릿
header('content-type:application/json; charset=utf-8');
// 응답의 콘텐츠 타입을 JSON으로 설정하고
// 인코딩을 UTF-8로 지정한다.
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
// 클라이언트가 사용할 수 있는 http 메서드 (POST, GET, OPTIONS)를 허용한다.
header("Access-Control-Allow-Headers : Content-Type");
// 클라이언트가 사용할 수 있는 HTTP 헤더 중에서 Content-Type 헤더를 허용한다.
if($_SERVER['REQUEST_METHOD'] === 'OPTIONS){
http_response_code(200);
exit();
}
// OPTIONS 요청이 들어오면, CORS (Cross-Origin Resource Sharing) 프리플라이트 요청을 처리하고
// 응답코드 200을 반환한 후에 스크립트를 종료한다.
// 프리플라이트는 나중에 설명하겠다!
// CORS 설정은 배포할때는 지우면 된다.
// 테스트할 때 도메인이 다르면 발생하는 CORS 에러를 잠시 없애기 위해서 사용하는 것이다.
$action = $_GET['action'] ?? '';
// URL의 GET 파라미터로 action을 가져오고, 없으면 빈 문자열을 설정한다.
// 없을 때의 에러처리도 해야겠지..? ( 여기서는 안함 )
if($_SERVER['REQUEST_METHOD'] === 'POST' && $action === 'reservedUser'){
}
// HTTP 요청이 POST이고, action 파라미터가 reserveUser 인 경우에만 블록의 코드를 실행한다.
$inputData = file_get_contents('php://input');
// HTTP 요청의 본문 데이터를 읽어온다.
// 일반적으로 JSON데이터를 처리할 때 사용한다.
$data = json_decode($inputData, true);
// JSON 형식의 본문 데이터를 PHP 배열로 변환한다.
$email = $data['email'] ?? '';
// 배열에서 email 값을 추출하고, 값이 없으면 빈 문자열을 생성한다.
if(!empty($email)){
get_reserved($email);
}else{
http_response_code(400);
echo json_encode(['message' => 'Email is required']);
}
// 이메일 값이 존재할 경우에 get_reserved() 함수를 호출하여, 이메일 값을 처리하고,
// 없으면 HTTP 400 응답코드 (잘못된 요청) 과 함께 Email is required라고 메시지를 반환한다.
else{
http_response_code(404);
echo json_encode(['message' => 'API not found']);
}
// POST 요청이 아니거나, action이 reserveUser가 아니면 HTTP 404 응답과 함께
// API not found 라고 메시지를 반환한다.
// 이제 아까 그 함수로 주어진 이메일 정보를 처리한다.
function get_reserved($email){
try{
$pdo = new PDO(~~~~~);
$PDO -> setAttribute(PDO:ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
// 데이터베이스에 연결하기 위해서 pdo 객체를 생성하고
// php가 예외처리하는게 default가 매우 특이 (ㅎㅎ)하기 때문에 오류 발생시에 예외를 던지게 처리한다.
$prequery = "
SELECT email FROM reserved_user WHERE email = :email
"
// 쿼리를 준비한다. xhr 공격을 방지하기 위해서 이따가 변수 바인딩을 할 것이다
$stmt = $pdo->prepare($preQuery);
$stmt -> bindParam(':email', $email);
$stmt -> execute();
// SQL 쿼리를 준비하고 :email 부분에 전달받은 이메일을 바인딩 한 후에 쿼리를 실행한다.
$result = $stmt -> fetch(PDO::FETCH_ASSOC);
// 쿼리 결과를 배열로 가져온다.
// FETCH_ASSOC은 데이터 가져오는 방식인데 여러가지가 있는데 블로그에 잘 정리되어있으니
// 찾아보길 바란다.
if(!empty($result)){
// 비어있지 않다면 들어온 이메일이 이미 테이블에 있다는 것이다.
http_response(409);
echo json_encode(['message' => 'Email is already reserved']);
// 이미 존재하는 이메일일 경우에 HTTP 409 응답 ( 중복된 데이터 )과 함께
// Email is already reserved 메시지를 반환한다.
else{
// 이메일이 존재하지 않을 경우에 테이블에 없다는 것이니 INSERT를 진행한다.
$query = "
INSERT INTO reserved_user
(email, createdat)
VALUES
(:email, CURRENT_TIME());
";
// 이메일을 데이터베이스에 삽입하기 위한 INSERT 쿼리를 준비한다.
// CURRENT_TIME()은 현재 시간을 자동으로 기록한다.
// 서버 기준이다.
$preparedstmt = $pdo->prepare($query);
$preparedstmt->bindParam(':email', $email);
$preparedstmt->execute();
// 아까 했듯이 SQL 쿼리를 준비하고, 이메일 변수를 바인딩해준 후에 쿼리를 실행하여 데이터를 삽입한다.
http_response_code(201);
echo json_encode(['message' => 'success']);
// 삽입이 성공하면 HTTP 201 응답 ( 생성되었다는...! ) 과 함께 success 메시지를 반환한다.
}
catch(PDOException $e){
// 데이터베이스 작업 중에 예외가 발생하면 이 블록이 실행한다.
// 아까 setAttribute에서 우리는 오류 발생시에 예외를 발생하도록 설정했다.
http_response_code(500);
echo json_encode(['message' => 'Database error: '.$e->getMessage()]);
// 예외가 발생하면 HTTP 500 응답 ( 서버 내부오류 )과 함께 오류 메시지를 반환한다.
}
그럼 아까 살펴보기로 했던 프리플라이트 Preflight 요청에 대해서 알아보자.
프리플라이트 요청은 CORS 규칙을 준수하기 위해서 클라이언트 ( 주로 브라우저다 어플리케이션이나 )가 서버에 미리 보내는 http 요청이다. 날기 전에 미리 날아서 테스트 한다는..? 으로 대충 기억하면 편하다.
이 요청은 실제 HTTP 요청 (POST, PUT, DELETE 등)이 서버로 전달되기 전에 특정 조건을 확인하기 위해서 보내진다.
문제가 있으면 바로 CORS 에러 내버린다.
그렇다면 CORS는 무엇일까? cross - origin resource sharing
- CORS는 웹 어플리케이션이 다른 도메인에서 제공되는 리소스를 액세스할 수 있도록 허용하는 메커니즘이다.
- 보안상의 이유로, 브라우저는 같은 출처(Origin)에서 제공된 리소스에만 기본적으로 접근할 수 있게 되어있다.
- 예를 들어서 http://example.com에서서 실행되는 자바스크립트는 기본적으로 http://api.example.com 에 접근할 수 없다.
- CORS 정책을 적용하면 다른 도메인에서의 요청도 허용할 수 있는데, 이 때 서버는 특정 헤더를 반환하여 클라이언트에게 이를 허용한다는 것을 명시해야 한다.
- 그런데, 모든 도메인을 허용한다고 설정해놓고 배포해버리면....... 예를 들어 aws에 api올렸는데, 누가 거따가 너 한번 요금 폭탄 맞아봐라하고 100 시간 내내 요청 계속 보내버리면, 대 참사다. 물론 서버도 제대로 응답이 필요한 사람에게 응답을 올바르게 못해줄 것이다.
header('Access-Control-Allow-Origin: *');
- 실제 배포할 때는 꼭 빼야한다. ( 카드 연결해놓은 서버 돌리는 프로젝트면 모두 다 )
다음으로는 프리플라이트 요청에 대해서 알아보자. preflight
- 프리플라이트 요청은 주로 '안전하지 않은' HTTP 메서드 (POST, PUT, DELETE 등)를 사용하거나, 커스텀 헤더가 포함된 요청을 보내기 전에 발생한다.
- 브라우저가 실제 요청을 보내기 전에 먼저 OPTIONS 메서드를 사용해서 서버가 해당 요청을 허용하는지 확인한다.
- 그래서 아까 우리는 OPTIONS HTTP메서드를 허용했다.
- 프리 플라이트 요청은 클라이언트(브라우저)가 서버에게 실제 요청이 가능한지, 어떤 메서드와 헤더가 허용되는 지를 물어보는 사전 요청이다.
그렇다면 대체 왜 프리 플라이트 요청을 굳이 하는걸까? 이거 왜 처리해야할까?
- 프리플라이트 요청을 처리하지 않으면, 브라우저는 요청을 보낼 수 없다.
- 특히 POST, PUT 같은 메서드나 커스텀 헤더 (Content-Type같은)를 사용할 때는 프리플라이트 요청을 반드시 처리해야 한다.
- 프리플라이트 요청이 실패하면 클라이언트는 실제 요청을 보내지 않고, 서버와 통신이 차단된다.
'Language > php' 카테고리의 다른 글
PHP 개발 참고 사항 (0) | 2025.01.31 |
---|---|
php 호스팅 후에 404 처리 feat chatgpt (2) | 2024.09.19 |
php자체에 대해서 알아보자 (7) | 2024.09.12 |
inc 파일 ... 대신에? (1) | 2024.09.12 |
PDO를 활용한 데이터베이스 연동 (2) | 2024.09.11 |