Ver3.0 ๐ PHP๋ก ๋ฐ์ดํฐ ํ์ฑํ๊ณ API ํต์ ํ๊ธฐ

๐ PHP๋ก ๋ฐ์ดํฐ ํ์ฑํ๊ณ API ํต์ ํ๊ธฐ
XML, JSON ์ ๋ณต๋ถํฐ ์ค์ API ์ฐ๋๊น์ง ์๋ฒฝ ๊ฐ์ด๋
๐ฏ ์ ๋ฐ์ดํฐ ํ์ฑ๊ณผ API ํต์ ์ ๋ฐฐ์์ผ ํ ๊น?
์๋
! ์ค๋์ PHP ๊ฐ๋ฐ์๋ผ๋ฉด ๋ฐ๋์ ์์์ผ ํ ํต์ฌ ์คํฌ, ๋ฐ๋ก ๋ฐ์ดํฐ ํ์ฑ๊ณผ API ํต์ ์ ๋ํด ์ด์ผ๊ธฐํด๋ณผ๊ฒ. ๐
์์ฆ ์น ๊ฐ๋ฐ์์ ํผ์ ๋ชจ๋ ๊ฑธ ๋ค ๋ง๋๋ ์๋๋ ์ง๋ฌ์ด. ๋ ์จ ์ ๋ณด๊ฐ ํ์ํ๋ฉด ๊ธฐ์์ฒญ API๋ฅผ ์ฐ๊ณ , ๊ฒฐ์ ๊ธฐ๋ฅ์ด ํ์ํ๋ฉด PG์ฌ API๋ฅผ ์ฐ๋ํ๊ณ , ์ง๋๊ฐ ํ์ํ๋ฉด ์นด์นด์ค๋งต์ด๋ ๊ตฌ๊ธ๋งต API๋ฅผ ๊ฐ์ ธ๋ค ์ฐ์ง. ์ด๋ฐ ์ธ๋ถ ์๋น์ค๋ค๊ณผ ๋ํํ๋ ค๋ฉด ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ๋ ๋ฐฉ๋ฒ์ ์์์ผ ํ๋๋ฐ, ๊ทธ๊ฒ ๋ฐ๋ก ์ค๋ ๋ฐฐ์ธ ๋ด์ฉ์ด์ผ! ๐
ํนํ ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ๋ค์ํ ์ธ๋ถ ์๋น์ค๋ฅผ ํตํฉํ๊ฑฐ๋, ํ๋ฆฌ๋์๋ก ํด๋ผ์ด์ธํธ ํ๋ก์ ํธ๋ฅผ ์งํํ ๋ ์ด ๊ธฐ์ ์ ์ ๋ง ์์ฃผ ์ฐ์ด๊ฑฐ๋ . ์ค๋ฌด์์ ๋ฐ๋ก ์จ๋จน์ ์ ์๋ ์ค์ฉ์ ์ธ ๋ด์ฉ๋ค๋ก ๊ฐ๋ ์ฑ์๋จ์ผ๋ ๋๊น์ง ์ง์คํด์ค! ๐ช
๐ฆ ๋ฐ์ดํฐ ํ์์ ๋ ๊ฑฐ์ฅ: XML vs JSON
API ํต์ ์์ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ๋ ๊ฐ์ฅ ๋ง์ด ์ฐ์ด๋ ํ์์ด ๋ฐ๋ก XML๊ณผ JSON์ด์ผ. ๋ ๋ค ๋ฐ์ดํฐ๋ฅผ ๊ตฌ์กฐํํด์ ์ ๋ฌํ๋ ๋ฐฉ์์ธ๋ฐ, ๊ฐ๊ฐ ํน์ง์ด ๋ฌ๋ผ์ ์ํฉ์ ๋ง๊ฒ ๊ณจ๋ผ ์ฐ๋ฉด ๋ผ. ๐ค
๐๏ธ XML (eXtensible Markup Language)
XML์ 1990๋
๋ ํ๋ฐ์ ๋ฑ์ฅํ ๋ฒ ํ
๋์ด์ผ. HTML์ฒ๋ผ ํ๊ทธ ๊ธฐ๋ฐ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ํํํ๋๋ฐ, ์ข ๋ ์๊ฒฉํ๊ณ ๊ตฌ์กฐ์ ์ด์ง.
XML์ ํน์ง:
โ
ํ๊ทธ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ธ๋ ๊ตฌ์กฐ
โ
๊ณ์ธต์ ๊ตฌ์กฐ ํํ์ ๊ฐํจ
โ
์คํค๋ง๋ฅผ ํตํ ์๊ฒฉํ ๊ฒ์ฆ ๊ฐ๋ฅ
โ
์ฃผ์ ์ถ๊ฐ ๊ฐ๋ฅ
โ
์์ฑ(attribute)๊ณผ ์์(element) ๋ชจ๋ ์ฌ์ฉ
โ ๋ฌธ๋ฒ์ด ์ฅํฉํด์ ํ์ผ ํฌ๊ธฐ๊ฐ ํผ
โ ํ์ฑ ์๋๊ฐ ์๋์ ์ผ๋ก ๋๋ฆผ
โก JSON (JavaScript Object Notation)
JSON์ 2000๋
๋ ์ด๋ฐ์ ๋ฑ์ฅํ ์ ์๋ก, ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด ํ๊ธฐ๋ฒ์ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ก์ด. ์์ฆ ๋๋ถ๋ถ์ API๊ฐ JSON์ ์ ํธํ์ง.
JSON์ ํน์ง:
โ
๊ฐ๊ฒฐํ๊ณ ์ฝ๊ธฐ ์ฌ์ด ๋ฌธ๋ฒ
โ
ํ์ผ ํฌ๊ธฐ๊ฐ ์์
โ
ํ์ฑ ์๋๊ฐ ๋น ๋ฆ
โ
์๋ฐ์คํฌ๋ฆฝํธ์ ์๋ฒฝํ ํธํ
โ
๋๋ถ๋ถ์ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์์ ์ง์
โ ์ฃผ์์ ๋ฌ ์ ์์
โ ๋ ์ง ํ์ ๋ฑ ์ผ๋ถ ๋ฐ์ดํฐ ํ์
๋ฏธ์ง์
๐ ์ค์ ๋ฐ์ดํฐ ๋น๊ต
๊ฐ์ ๋ฐ์ดํฐ๋ฅผ XML๊ณผ JSON์ผ๋ก ํํํ๋ฉด ์ด๋ ๊ฒ ๋ฌ๋ผ์ ธ:
<?xml version="1.0" encoding="UTF-8"?>
<user>
<id>1001</id>
<name>๊น๊ฐ๋ฐ</name>
<email>dev@example.com</email>
<skills>
<skill>PHP</skill>
<skill>JavaScript</skill>
<skill>MySQL</skill>
</skills>
<active>true</active>
</user>
{
"id": 1001,
"name": "๊น๊ฐ๋ฐ",
"email": "dev@example.com",
"skills": ["PHP", "JavaScript", "MySQL"],
"active": true
}
๋ณด๋ค์ํผ JSON์ด ํจ์ฌ ๊ฐ๊ฒฐํ์ง? ๊ฐ์ ์ ๋ณด๋ฅผ ๋ด๋๋ฐ XML์ ์ฝ 250๋ฐ์ดํธ, JSON์ ์ฝ 120๋ฐ์ดํธ ์ ๋์ผ. ๊ฑฐ์ ์ ๋ฐ ์์ค! ๐ฏ
๐ PHP์์ XML ํ์ฑํ๊ธฐ
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก PHP์์ XML ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ์ ์์๋ณผ๊ฒ. PHP๋ XML์ ์ฒ๋ฆฌํ๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋๋ฐ, ์ํฉ์ ๋ง๊ฒ ๊ณจ๋ผ ์ฐ๋ฉด ๋ผ! ๐ ๏ธ
1๏ธโฃ SimpleXML - ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ
์ด๋ฆ์ฒ๋ผ ์ ๋ง ์ฌํํด! ์์ XML ํ์ผ์ ๋น ๋ฅด๊ฒ ํ์ฑํ ๋ ์ต๊ณ ์ผ. PHP 5๋ถํฐ ๊ธฐ๋ณธ ๋ด์ฅ๋์ด ์์ด์ ๋ณ๋ ์ค์น ์์ด ๋ฐ๋ก ์ธ ์ ์์ด.
<?php
// XML ๋ฌธ์์ด์ ๊ฐ์ฒด๋ก ๋ณํ
$xmlString = '<?xml version="1.0"?>
<bookstore>
<book category="web">
<title lang="ko">PHP ๋ง์คํฐํ๊ธฐ</title>
<author>ํ๊ธธ๋</author>
<year>2024</year>
<price>35000</price>
</book>
<book category="database">
<title lang="ko">MySQL ์๋ฒฝ๊ฐ์ด๋</title>
<author>๊น๋ฐ์ดํฐ</author>
<year>2023</year>
<price>42000</price>
</book>
</bookstore>';
$xml = simplexml_load_string($xmlString);
// ๋ฐ์ดํฐ ์ ๊ทผํ๊ธฐ
echo $xml->book[0]->title; // "PHP ๋ง์คํฐํ๊ธฐ"
echo $xml->book[0]->author; // "ํ๊ธธ๋"
// ์์ฑ ์ ๊ทผํ๊ธฐ
echo $xml->book[0]['category']; // "web"
echo $xml->book[0]->title['lang']; // "ko"
// ๋ฐ๋ณต๋ฌธ์ผ๋ก ๋ชจ๋ ์ฑ
์ถ๋ ฅ
foreach ($xml->book as $book) {
echo "์ ๋ชฉ: " . $book->title . "\n";
echo "์ ์: " . $book->author . "\n";
echo "๊ฐ๊ฒฉ: " . number_format($book->price) . "์\n";
echo "---\n";
}
?>
โข simplexml_load_file(): XML ํ์ผ์ ์ง์ ๋ก๋
โข ๊ฐ์ฒด์ฒ๋ผ ์ ๊ทผ ๊ฐ๋ฅํด์ ์ง๊ด์ ์ด์ผ!
โข ์์ ํ์ผ์ ์ต์ ํ๋์ด ์์ด (์ MB ์ดํ)
2๏ธโฃ DOMDocument - ๊ฐ๋ ฅํ ํ์
๋ณต์กํ XML ๊ตฌ์กฐ๋ฅผ ๋ค๋ฃจ๊ฑฐ๋, XML์ ์์ /์์ฑํด์ผ ํ ๋๋ DOMDocument๊ฐ ๋ต์ด์ผ. W3C DOM ํ์ค์ ๋ฐ๋ฅด๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ์ธ์ด์์๋ ๋น์ทํ๊ฒ ์ฌ์ฉํ ์ ์์ด. ๐ช
<?php
$xmlString = '<?xml version="1.0" encoding="UTF-8"?>
<products>
<product id="101">
<name>๋
ธํธ๋ถ</name>
<price currency="KRW">1500000</price>
<stock>25</stock>
</product>
<product id="102">
<name>๋ง์ฐ์ค</name>
<price currency="KRW">35000</price>
<stock>150</stock>
</product>
</products>';
// DOMDocument ๊ฐ์ฒด ์์ฑ
$dom = new DOMDocument();
$dom->loadXML($xmlString);
// XPath๋ฅผ ์ฌ์ฉํ ๊ฒ์ (๋งค์ฐ ๊ฐ๋ ฅ!)
$xpath = new DOMXPath($dom);
// ๊ฐ๊ฒฉ์ด 100๋ง์ ์ด์์ธ ์ ํ ์ฐพ๊ธฐ
$expensiveProducts = $xpath->query("//product[price > 1000000]");
foreach ($expensiveProducts as $product) {
$name = $product->getElementsByTagName('name')->item(0)->nodeValue;
$price = $product->getElementsByTagName('price')->item(0)->nodeValue;
echo "๊ณ ๊ฐ ์ ํ: {$name} - " . number_format($price) . "์\n";
}
// ์๋ก์ด ์ ํ ์ถ๊ฐํ๊ธฐ
$newProduct = $dom->createElement('product');
$newProduct->setAttribute('id', '103');
$name = $dom->createElement('name', 'ํค๋ณด๋');
$price = $dom->createElement('price', '89000');
$price->setAttribute('currency', 'KRW');
$stock = $dom->createElement('stock', '80');
$newProduct->appendChild($name);
$newProduct->appendChild($price);
$newProduct->appendChild($stock);
$dom->documentElement->appendChild($newProduct);
// ์์ ๋ XML ์ ์ฅ
$dom->save('products.xml');
// ๋๋ ๋ฌธ์์ด๋ก ์ถ๋ ฅ
echo $dom->saveXML();
?>
3๏ธโฃ XMLReader - ๋์ฉ๋ ํ์ผ์ ๊ตฌ์ธ์ฃผ
์๋ฐฑ MB, ์ฌ์ง์ด GB ๋จ์์ ๊ฑฐ๋ํ XML ํ์ผ์ ๋ค๋ค์ผ ํ๋ค๋ฉด? XMLReader๊ฐ ์ ๋ต์ด์ผ! ํ์ผ์ ํ ๋ฒ์ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฌ์ง ์๊ณ ์์ฐจ์ ์ผ๋ก ์ฝ์ด๋๊ฐ๊ฑฐ๋ . ๐
<?php
// ๋์ฉ๋ ๋ก๊ทธ ํ์ผ ์ฒ๋ฆฌ ์์
$reader = new XMLReader();
$reader->open('huge_log_file.xml');
$errorCount = 0;
$warningCount = 0;
// ์คํธ๋ฆผ ๋ฐฉ์์ผ๋ก ์ฝ๊ธฐ
while ($reader->read()) {
// 'log' ์์๋ฅผ ์ฐพ์ผ๋ฉด
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'log') {
// ํ์ฌ ๋
ธ๋๋ฅผ SimpleXML๋ก ๋ณํ (ํธ์์ฑ)
$node = simplexml_import_dom($reader->expand());
$level = (string)$node['level'];
$message = (string)$node->message;
$timestamp = (string)$node->timestamp;
if ($level == 'ERROR') {
$errorCount++;
echo "[ERROR] {$timestamp}: {$message}\n";
} elseif ($level == 'WARNING') {
$warningCount++;
}
}
}
$reader->close();
echo "\n์ด ์๋ฌ: {$errorCount}๊ฑด\n";
echo "์ด ๊ฒฝ๊ณ : {$warningCount}๊ฑด\n";
?>
โข XML ํ์ผ์ด ์์ ๋ (1~2MB ์ดํ)
โข ๋น ๋ฅด๊ฒ ํ๋กํ ํ์ ์ ๋ง๋ค ๋
โข ์ฝ๊ธฐ๋ง ํ๋ฉด ๋ ๋
DOMDocument๋ฅผ ์ธ ๋:
โข XML์ ์์ /์์ฑํด์ผ ํ ๋
โข ๋ณต์กํ ๊ฒ์์ด ํ์ํ ๋ (XPath)
โข ์ ํํ ๊ตฌ์กฐ ๊ฒ์ฆ์ด ํ์ํ ๋
XMLReader๋ฅผ ์ธ ๋:
โข ํ์ผ์ด ๋งค์ฐ ํด ๋ (์์ญ MB ์ด์)
โข ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ ํ์ ์ผ ๋
โข ํน์ ๋ฐ์ดํฐ๋ง ์ถ์ถํ๋ฉด ๋ ๋
โก PHP์์ JSON ํ์ฑํ๊ธฐ
JSON์ XML๋ณด๋ค ํจ์ฌ ๊ฐ๋จํด! PHP๋ JSON์ ๋ค๋ฃจ๋ ๊ฐ๋ ฅํ ๋ด์ฅ ํจ์๋ค์ ์ ๊ณตํ๊ฑฐ๋ . ๋ฑ ๋ ๊ฐ๋ง ์๋ฉด ๋ผ: json_encode()์ json_decode()! ๐
๐ json_decode() - JSON์ PHP๋ก
JSON ๋ฌธ์์ด์ PHP์์ ์ฌ์ฉํ ์ ์๋ ํํ๋ก ๋ณํํด์ค. ๊ฐ์ฒด๋ ๋ฐฐ์ด๋ก ๋ฐ์ ์ ์์ด!
<?php
$jsonString = '{
"user": {
"id": 2024,
"username": "phpmaster",
"email": "master@example.com",
"profile": {
"age": 28,
"city": "์์ธ",
"skills": ["PHP", "MySQL", "JavaScript", "React"]
},
"premium": true,
"points": 15000
}
}';
// ๊ฐ์ฒด๋ก ๋ณํ (๊ธฐ๋ณธ๊ฐ)
$data = json_decode($jsonString);
echo $data->user->username; // "phpmaster"
echo $data->user->profile->city; // "์์ธ"
echo $data->user->profile->skills[0]; // "PHP"
// ์ฐ๊ด ๋ฐฐ์ด๋ก ๋ณํ (๋ ๋ฒ์งธ ํ๋ผ๋ฏธํฐ๋ฅผ true๋ก)
$dataArray = json_decode($jsonString, true);
echo $dataArray['user']['username']; // "phpmaster"
echo $dataArray['user']['profile']['city']; // "์์ธ"
echo $dataArray['user']['profile']['skills'][0]; // "PHP"
// ์คํฌ ๋ชฉ๋ก ์ถ๋ ฅ
foreach ($dataArray['user']['profile']['skills'] as $skill) {
echo "- {$skill}\n";
}
?>
โ ์ฝ๋๊ฐ ๊น๋ํ๊ณ ์ฝ๊ธฐ ์ฌ์
โ IDE ์๋์์ฑ ์ง์ ๊ฐ๋ฅ
โ ๋์ ํค ์ ๊ทผ์ด ์ด๋ ค์
๋ฐฐ์ด ๋ฐฉ์ ($data['user']['name']):
โ ๋์ ํค ์ ๊ทผ ์ฌ์
โ ๋ฐฐ์ด ํจ์ ์ฌ์ฉ ๊ฐ๋ฅ
โ ํค ์กด์ฌ ์ฌ๋ถ ์ฒดํฌ ํธํจ (isset)
๊ฐ์ธ์ ์ผ๋ก๋ API ์๋ต ์ฒ๋ฆฌํ ๋ ๋ฐฐ์ด ๋ฐฉ์์ ์ ํธํด. ๋ ์ ์ฐํ๊ฑฐ๋ ! ๐ฏ
๐ json_encode() - PHP๋ฅผ JSON์ผ๋ก
PHP ๋ฐฐ์ด์ด๋ ๊ฐ์ฒด๋ฅผ JSON ๋ฌธ์์ด๋ก ๋ณํํด์ค. API ์๋ต์ ๋ง๋ค ๋ ํ์์ผ!
<?php
// PHP ๋ฐฐ์ด ์ค๋น
$response = [
'status' => 'success',
'code' => 200,
'data' => [
'products' => [
[
'id' => 1,
'name' => 'ํ๋ฆฌ๋ฏธ์ ํค๋ณด๋',
'price' => 159000,
'inStock' => true,
'tags' => ['๊ธฐ๊ณ์', '๋ฌด์ ', 'RGB']
],
[
'id' => 2,
'name' => '๊ฒ์ด๋ฐ ๋ง์ฐ์ค',
'price' => 89000,
'inStock' => false,
'tags' => ['์ ์ ', '๊ณ DPI']
]
],
'total' => 2
],
'timestamp' => date('Y-m-d H:i:s')
];
// ๊ธฐ๋ณธ JSON ๋ณํ
$json = json_encode($response);
echo $json;
// ์์๊ฒ ์ถ๋ ฅ (๋ค์ฌ์ฐ๊ธฐ ํฌํจ)
$prettyJson = json_encode($response, JSON_PRETTY_PRINT);
echo $prettyJson;
// ํ๊ธ ์ ๋์ฝ๋ ์ด์ค์ผ์ดํ ๋ฐฉ์ง
$koreanJson = json_encode($response, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
echo $koreanJson;
// ์ฌ๋์ ์ด์ค์ผ์ดํ ๋ฐฉ์ง
$urlJson = json_encode($response, JSON_UNESCAPED_SLASHES);
?>
๐จ json_encode() ์ต์ ์๋ฒฝ ์ ๋ฆฌ
| ์ต์ | ์ค๋ช | ์ฌ์ฉ ์๊ธฐ |
|---|---|---|
| JSON_PRETTY_PRINT | ๋ณด๊ธฐ ์ข๊ฒ ๋ค์ฌ์ฐ๊ธฐ | ๋๋ฒ๊น , ๋ก๊ทธ ํ์ผ |
| JSON_UNESCAPED_UNICODE | ํ๊ธ ๋ฑ ์ ๋์ฝ๋ ๊ทธ๋๋ก ์ถ๋ ฅ | ํ๊ธ ๋ฐ์ดํฐ ์ฒ๋ฆฌ |
| JSON_UNESCAPED_SLASHES | ์ฌ๋์(/) ์ด์ค์ผ์ดํ ์ ํจ | URL ํฌํจ ๋ฐ์ดํฐ |
| JSON_NUMERIC_CHECK | ์ซ์ ๋ฌธ์์ด์ ์ซ์๋ก ๋ณํ | DB ๋ฐ์ดํฐ ์ฒ๋ฆฌ |
| JSON_FORCE_OBJECT | ๋ฐฐ์ด์ ๊ฐ์ฒด๋ก ๊ฐ์ ๋ณํ | ๋น ๋ฐฐ์ด ์ฒ๋ฆฌ |
์ฌ๋ฌ ์ต์
์ ๋์์ ์ธ ๋๋ ํ์ดํ(|)๋ก ์ฐ๊ฒฐํด:
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
๐จ JSON ์๋ฌ ์ฒ๋ฆฌ
JSON ํ์ฑ์ด ์คํจํ ์ ์์ด. ์๋ชป๋ ํ์์ด๊ฑฐ๋ ์ธ์ฝ๋ฉ ๋ฌธ์ ๊ฐ ์์ ๋ ๋ง์ด์ผ. ํญ์ ์๋ฌ ์ฒดํฌ๋ฅผ ํด์ค์ผ ํด! ๐ก๏ธ
<?php
function safeJsonDecode($jsonString, $assoc = true) {
// JSON ๋์ฝ๋ฉ ์๋
$data = json_decode($jsonString, $assoc);
// ์๋ฌ ์ฒดํฌ
if (json_last_error() !== JSON_ERROR_NONE) {
$errorMsg = json_last_error_msg();
// ์๋ฌ ํ์
๋ณ ์ฒ๋ฆฌ
switch (json_last_error()) {
case JSON_ERROR_DEPTH:
$error = '์ต๋ ์คํ ๊น์ด ์ด๊ณผ';
break;
case JSON_ERROR_STATE_MISMATCH:
$error = '์ธ๋ํ๋ก์ฐ ๋๋ ๋ชจ๋ ๋ถ์ผ์น';
break;
case JSON_ERROR_CTRL_CHAR:
$error = '์ ์ด ๋ฌธ์ ์๋ฌ';
break;
case JSON_ERROR_SYNTAX:
$error = 'JSON ๋ฌธ๋ฒ ์๋ฌ';
break;
case JSON_ERROR_UTF8:
$error = 'UTF-8 ์ธ์ฝ๋ฉ ์๋ฌ';
break;
default:
$error = '์ ์ ์๋ ์๋ฌ';
break;
}
throw new Exception("JSON ํ์ฑ ์คํจ: {$error} - {$errorMsg}");
}
return $data;
}
// ์ฌ์ฉ ์์
try {
$jsonString = '{"name": "ํ
์คํธ", "invalid": }'; // ์๋ชป๋ JSON
$data = safeJsonDecode($jsonString);
print_r($data);
} catch (Exception $e) {
echo "์๋ฌ ๋ฐ์: " . $e->getMessage();
// ๋ก๊ทธ ๊ธฐ๋ก, ์๋ฆผ ๋ฐ์ก ๋ฑ ์ถ๊ฐ ์ฒ๋ฆฌ
}
?>
โ ํด๊ฒฐ: mb_convert_encoding($str, 'UTF-8', 'EUC-KR')
2. BOM(Byte Order Mark) ๋ฌธ์ : ํ์ผ ์์์ ์จ๊ฒจ์ง ๋ฌธ์
โ ํด๊ฒฐ: $json = preg_replace('/^\xEF\xBB\xBF/', '', $json)
3. ํน์๋ฌธ์ ์ด์ค์ผ์ดํ ๋๋ฝ: ๋ฐ์ดํ, ๋ฐฑ์ฌ๋์ ๋ฑ
โ ํด๊ฒฐ: ํญ์ json_encode() ์ฌ์ฉ, ์ง์ ๋ฌธ์์ด ์กฐํฉ ๊ธ์ง!
๐ HTTP ์์ฒญ์ ๊ธฐ์ด - GET vs POST
API ํต์ ์ ํ๋ ค๋ฉด HTTP ์์ฒญ์ ๋ณด๋ผ ์ค ์์์ผ ํด. ๊ฐ์ฅ ๊ธฐ๋ณธ์ด ๋๋ GET๊ณผ POST ๋ฐฉ์๋ถํฐ ์ ๋๋ก ์ดํดํด๋ณด์! ๐ก
๐ฅ GET ์์ฒญ - ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
GET์ ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ๋ ์จ. URL์ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ถ์ฌ์ ๋ณด๋ด๋ ๋ฐฉ์์ด์ผ.
ํน์ง:
โ
URL์ ๋ฐ์ดํฐ๊ฐ ๋
ธ์ถ๋จ (์ฟผ๋ฆฌ์คํธ๋ง)
โ
๋ธ๋ผ์ฐ์ ํ์คํ ๋ฆฌ์ ๋จ์
โ
๋ถ๋งํฌ ๊ฐ๋ฅ
โ
์บ์ฑ ๊ฐ๋ฅ
โ ๋ฐ์ดํฐ ๊ธธ์ด ์ ํ (๋ณดํต 2048์)
โ ๋ฏผ๊ฐํ ์ ๋ณด ์ ์ก ๋ถ์ ํฉ
์ฌ์ฉ ์์:
โข ๊ฒ์ ๊ธฐ๋ฅ
โข ๋ชฉ๋ก ์กฐํ
โข ์์ธ ์ ๋ณด ๋ณด๊ธฐ
โข ๊ณต๊ฐ API ํธ์ถ
๐ค POST ์์ฒญ - ๋ฐ์ดํฐ ์ ์กํ๊ธฐ
POST๋ ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ฑฐ๋ ์ฒ๋ฆฌ๋ฅผ ์์ฒญํ ๋ ์จ. ๋ฐ์ดํฐ๋ฅผ HTTP Body์ ๋ด์ ๋ณด๋ด์ง.
ํน์ง:
โ
URL์ ๋ฐ์ดํฐ๊ฐ ๋
ธ์ถ๋์ง ์์
โ
๋ฐ์ดํฐ ๊ธธ์ด ์ ํ ์์
โ
ํ์ผ ์
๋ก๋ ๊ฐ๋ฅ
โ
๋ฏผ๊ฐํ ์ ๋ณด ์ ์ก ๊ฐ๋ฅ
โ ๋ธ๋ผ์ฐ์ ํ์คํ ๋ฆฌ์ ์ ๋จ์
โ ๋ถ๋งํฌ ๋ถ๊ฐ
์ฌ์ฉ ์์:
โข ๋ก๊ทธ์ธ/ํ์๊ฐ์
โข ๋ฐ์ดํฐ ์์ฑ/์์ /์ญ์
โข ํ์ผ ์
๋ก๋
โข ๊ฒฐ์ ์ฒ๋ฆฌ
โข GET: ์กฐํ (Read)
โข POST: ์์ฑ (Create)
โข PUT: ์ ์ฒด ์์ (Update)
โข PATCH: ๋ถ๋ถ ์์ (Partial Update)
โข DELETE: ์ญ์ (Delete)
์ด๊ฑธ CRUD(Create, Read, Update, Delete)๋ผ๊ณ ๋ถ๋ฅด์ง! ๐
๐ PHP์์ API ํธ์ถํ๊ธฐ - cURL ๋ง์คํฐ
์, ์ด์ ์ง์ง ํต์ฌ์ด์ผ! PHP์์ ์ธ๋ถ API๋ฅผ ํธ์ถํ๋ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๋๊ตฌ, cURL์ ๋ง์คํฐํด๋ณด์. cURL์ ๋ค์ํ ํ๋กํ ์ฝ์ ์ง์ํ๋ ๋ฐ์ดํฐ ์ ์ก ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ผ. ๐ช
๐ง cURL ๊ธฐ๋ณธ ๊ตฌ์กฐ
cURL ์ฌ์ฉ์ ํญ์ 4๋จ๊ณ๋ก ์ด๋ฃจ์ด์ ธ:
<?php
// 1๋จ๊ณ: cURL ์ธ์
์ด๊ธฐํ
$ch = curl_init();
// 2๋จ๊ณ: ์ต์
์ค์
curl_setopt($ch, CURLOPT_URL, "https://api.example.com/data");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// 3๋จ๊ณ: ์คํ
$response = curl_exec($ch);
// 4๋จ๊ณ: ์ธ์
์ข
๋ฃ
curl_close($ch);
// ๊ฒฐ๊ณผ ์ฒ๋ฆฌ
echo $response;
?>
๐ก GET ์์ฒญ ์๋ฒฝ ์์
<?php
function apiGetRequest($url, $params = []) {
// URL์ ์ฟผ๋ฆฌ์คํธ๋ง ์ถ๊ฐ
if (!empty($params)) {
$url .= '?' . http_build_query($params);
}
// cURL ์ด๊ธฐํ
$ch = curl_init();
// ์ต์
์ค์
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true, // ๊ฒฐ๊ณผ๋ฅผ ๋ฌธ์์ด๋ก ๋ฐํ
CURLOPT_FOLLOWLOCATION => true, // ๋ฆฌ๋ค์ด๋ ํธ ์๋ ๋ฐ๋ผ๊ฐ๊ธฐ
CURLOPT_TIMEOUT => 30, // ํ์์์ 30์ด
CURLOPT_SSL_VERIFYPEER => true, // SSL ์ธ์ฆ์ ๊ฒ์ฆ
CURLOPT_USERAGENT => 'PHP cURL Client/1.0', // User-Agent ์ค์
CURLOPT_HTTPHEADER => [
'Accept: application/json',
'Content-Type: application/json'
]
]);
// ์คํ
$response = curl_exec($ch);
// ์๋ฌ ์ฒดํฌ
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
throw new Exception("cURL ์๋ฌ: {$error}");
}
// HTTP ์ํ ์ฝ๋ ํ์ธ
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception("HTTP ์๋ฌ: {$httpCode}");
}
return json_decode($response, true);
}
// ์ฌ์ฉ ์์: ๋ ์จ API ํธ์ถ
try {
$weatherData = apiGetRequest('https://api.weather.com/v1/current', [
'city' => 'Seoul',
'apikey' => 'your_api_key_here',
'units' => 'metric'
]);
echo "ํ์ฌ ์จ๋: {$weatherData['temp']}ยฐC\n";
echo "๋ ์จ: {$weatherData['description']}\n";
} catch (Exception $e) {
echo "์๋ฌ: " . $e->getMessage();
}
?>
๐ค POST ์์ฒญ ์๋ฒฝ ์์
<?php
function apiPostRequest($url, $data, $headers = []) {
// JSON ์ธ์ฝ๋ฉ
$jsonData = json_encode($data);
// ๊ธฐ๋ณธ ํค๋ ์ค์
$defaultHeaders = [
'Content-Type: application/json',
'Content-Length: ' . strlen($jsonData)
];
$headers = array_merge($defaultHeaders, $headers);
// cURL ์ด๊ธฐํ
$ch = curl_init();
// ์ต์
์ค์
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true, // POST ๋ฉ์๋ ์ฌ์ฉ
CURLOPT_POSTFIELDS => $jsonData, // ์ ์กํ ๋ฐ์ดํฐ
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => true
]);
// ์คํ
$response = curl_exec($ch);
// ์๋ฌ ์ฒดํฌ
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
throw new Exception("cURL ์๋ฌ: {$error}");
}
// HTTP ์ํ ์ฝ๋ ํ์ธ
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// ์ํ ์ฝ๋๋ณ ์ฒ๋ฆฌ
if ($httpCode < 200 || $httpCode >= 300) {
throw new Exception("HTTP ์๋ฌ {$httpCode}: {$response}");
}
return json_decode($response, true);
}
// ์ฌ์ฉ ์์: ์ฌ์ฉ์ ์์ฑ API
try {
$newUser = [
'username' => 'newuser123',
'email' => 'user@example.com',
'password' => password_hash('secret123', PASSWORD_DEFAULT),
'profile' => [
'name' => 'ํ๊ธธ๋',
'age' => 25,
'city' => '์์ธ'
]
];
$result = apiPostRequest(
'https://api.example.com/users',
$newUser,
['Authorization: Bearer your_token_here']
);
echo "์ฌ์ฉ์ ์์ฑ ์ฑ๊ณต! ID: {$result['user_id']}\n";
} catch (Exception $e) {
echo "์๋ฌ: " . $e->getMessage();
}
?>
โข CURLOPT_URL: ์์ฒญํ URL
โข CURLOPT_RETURNTRANSFER: true๋ก ์ค์ ํ๋ฉด ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ
๋ฉ์๋ ๊ด๋ จ:
โข CURLOPT_POST: POST ์์ฒญ
โข CURLOPT_CUSTOMREQUEST: PUT, DELETE ๋ฑ ์ปค์คํ ๋ฉ์๋
โข CURLOPT_POSTFIELDS: ์ ์กํ ๋ฐ์ดํฐ
๋ณด์ ๊ด๋ จ:
โข CURLOPT_SSL_VERIFYPEER: SSL ์ธ์ฆ์ ๊ฒ์ฆ (์ด์: true, ๊ฐ๋ฐ: false)
โข CURLOPT_SSL_VERIFYHOST: ํธ์คํธ ๊ฒ์ฆ (2 ๊ถ์ฅ)
ํ์์์:
โข CURLOPT_TIMEOUT: ์ ์ฒด ํ์์์ (์ด)
โข CURLOPT_CONNECTTIMEOUT: ์ฐ๊ฒฐ ํ์์์ (์ด)
๊ธฐํ:
โข CURLOPT_FOLLOWLOCATION: ๋ฆฌ๋ค์ด๋ ํธ ์๋ ๋ฐ๋ผ๊ฐ๊ธฐ
โข CURLOPT_HTTPHEADER: ์ปค์คํ ํค๋ ๋ฐฐ์ด
โข CURLOPT_USERAGENT: User-Agent ๋ฌธ์์ด
๐ ์ธ์ฆ์ด ํ์ํ API ํธ์ถ
๋๋ถ๋ถ์ ์ค๋ฌด API๋ ์ธ์ฆ์ด ํ์ํด. ์ฃผ์ ์ธ์ฆ ๋ฐฉ์๋ค์ ์์๋ณด์! ๐
<?php
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.example.com/data',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'X-API-Key: your_api_key_here',
'Accept: application/json'
]
]);
$response = curl_exec($ch);
curl_close($ch);
?>
<?php
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.example.com/user/profile',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
'Accept: application/json'
]
]);
$response = curl_exec($ch);
curl_close($ch);
?>
<?php
$username = 'your_username';
$password = 'your_password';
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://api.example.com/data',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERPWD => "{$username}:{$password}",
CURLOPT_HTTPAUTH => CURLAUTH_BASIC
]);
$response = curl_exec($ch);
curl_close($ch);
?>
๐ ํ์ผ ์ ๋ก๋ API
<?php
function uploadFile($url, $filePath, $fieldName = 'file') {
// ํ์ผ ์กด์ฌ ํ์ธ
if (!file_exists($filePath)) {
throw new Exception("ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค: {$filePath}");
}
// CURLFile ๊ฐ์ฒด ์์ฑ (PHP 5.5+)
$cfile = new CURLFile(
$filePath,
mime_content_type($filePath),
basename($filePath)
);
// POST ๋ฐ์ดํฐ ์ค๋น
$postData = [
$fieldName => $cfile,
'description' => '์
๋ก๋๋ ํ์ผ',
'category' => 'documents'
];
// cURL ์ค์
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $postData,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer your_token_here'
]
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception(curl_error($ch));
}
curl_close($ch);
return json_decode($response, true);
}
// ์ฌ์ฉ ์์
try {
$result = uploadFile(
'https://api.example.com/upload',
'/path/to/document.pdf',
'document'
);
echo "์
๋ก๋ ์ฑ๊ณต! ํ์ผ ID: {$result['file_id']}\n";
echo "URL: {$result['url']}\n";
} catch (Exception $e) {
echo "์
๋ก๋ ์คํจ: " . $e->getMessage();
}
?>
โก file_get_contents() - ๊ฐ๋จํ ๋์
cURL์ด ๋๋ฌด ๋ณต์กํ๊ฒ ๋๊ปด์ง๋ค๋ฉด? ๊ฐ๋จํ GET ์์ฒญ์๋ file_get_contents()๋ฅผ ์จ๋ ๋ผ! ํจ์ฌ ๊ฐ๊ฒฐํ๊ฑฐ๋ . ๐
<?php
// ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ
$response = file_get_contents('https://api.example.com/data');
$data = json_decode($response, true);
// ์ต์
์ ์ถ๊ฐํ ๋ฐฉ๋ฒ
$options = [
'http' => [
'method' => 'GET',
'header' => [
'Accept: application/json',
'User-Agent: PHP Client/1.0'
],
'timeout' => 10
]
];
$context = stream_context_create($options);
$response = file_get_contents('https://api.example.com/data', false, $context);
$data = json_decode($response, true);
?>
<?php
$postData = [
'username' => 'testuser',
'email' => 'test@example.com'
];
$options = [
'http' => [
'method' => 'POST',
'header' => [
'Content-Type: application/json',
'Authorization: Bearer your_token'
],
'content' => json_encode($postData),
'timeout' => 10
]
];
$context = stream_context_create($options);
$response = file_get_contents('https://api.example.com/users', false, $context);
// HTTP ์๋ต ์ฝ๋ ํ์ธ
$statusLine = $http_response_header[0];
preg_match('/\d{3}/', $statusLine, $matches);
$statusCode = $matches[0];
if ($statusCode == 200) {
$data = json_decode($response, true);
print_r($data);
} else {
echo "์๋ฌ: HTTP {$statusCode}\n";
}
?>
โ ๊ฐ๋จํ GET ์์ฒญ
โ ๋น ๋ฅธ ํ๋กํ ํ์
โ ์ธ๋ถ ์์กด์ฑ ์ต์ํ
โ ์์ ํ๋ก์ ํธ
cURL์ ์ธ ๋:
โ ๋ณต์กํ ์ธ์ฆ์ด ํ์ํ ๋
โ ํ์ผ ์ ๋ก๋
โ ์ธ๋ฐํ ์ ์ด๊ฐ ํ์ํ ๋
โ ์๋ฌ ์ฒ๋ฆฌ๊ฐ ์ค์ํ ๋
โ ํ๋ก๋์ ํ๊ฒฝ
๊ฐ์ธ์ ์ผ๋ก๋ ์ค๋ฌด์์๋ cURL์ ์ถ์ฒํด. ๋ ์์ ์ ์ด๊ณ ๊ธฐ๋ฅ์ด ๋ง๊ฑฐ๋ ! ๐ช
๐จ ์ค์ ํ๋ก์ ํธ: ๋ ์จ API ํตํฉํ๊ธฐ
์ด๋ก ์ ์ถฉ๋ถํ ๋ฐฐ์ ์ผ๋, ์ด์ ์ค์ ๋ก ๋์ํ๋ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด์! OpenWeatherMap API๋ฅผ ์ฌ์ฉํด์ ๋ ์จ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ์์ ํ ์์ ์ผ. ๐ค๏ธ
<?php
class WeatherAPI {
private $apiKey;
private $baseUrl = 'https://api.openweathermap.org/data/2.5';
private $cacheDir = './cache';
private $cacheTime = 1800; // 30๋ถ
public function __construct($apiKey) {
$this->apiKey = $apiKey;
// ์บ์ ๋๋ ํ ๋ฆฌ ์์ฑ
if (!is_dir($this->cacheDir)) {
mkdir($this->cacheDir, 0755, true);
}
}
/**
* ํ์ฌ ๋ ์จ ์กฐํ
*/
public function getCurrentWeather($city, $units = 'metric') {
// ์บ์ ํ์ธ
$cacheKey = md5("current_{$city}_{$units}");
$cachedData = $this->getCache($cacheKey);
if ($cachedData !== null) {
return $cachedData;
}
// API ํธ์ถ
$url = "{$this->baseUrl}/weather";
$params = [
'q' => $city,
'appid' => $this->apiKey,
'units' => $units,
'lang' => 'kr'
];
$data = $this->makeRequest($url, $params);
// ๋ฐ์ดํฐ ๊ฐ๊ณต
$weather = [
'city' => $data['name'],
'country' => $data['sys']['country'],
'temperature' => round($data['main']['temp'], 1),
'feels_like' => round($data['main']['feels_like'], 1),
'humidity' => $data['main']['humidity'],
'pressure' => $data['main']['pressure'],
'description' => $data['weather'][0]['description'],
'icon' => $data['weather'][0]['icon'],
'wind_speed' => $data['wind']['speed'],
'timestamp' => $data['dt'],
'sunrise' => $data['sys']['sunrise'],
'sunset' => $data['sys']['sunset']
];
// ์บ์ ์ ์ฅ
$this->setCache($cacheKey, $weather);
return $weather;
}
/**
* 5์ผ ์๋ณด ์กฐํ
*/
public function getForecast($city, $units = 'metric') {
$cacheKey = md5("forecast_{$city}_{$units}");
$cachedData = $this->getCache($cacheKey);
if ($cachedData !== null) {
return $cachedData;
}
$url = "{$this->baseUrl}/forecast";
$params = [
'q' => $city,
'appid' => $this->apiKey,
'units' => $units,
'lang' => 'kr'
];
$data = $this->makeRequest($url, $params);
// ์ผ๋ณ๋ก ๊ทธ๋ฃนํ
$forecast = [];
foreach ($data['list'] as $item) {
$date = date('Y-m-d', $item['dt']);
if (!isset($forecast[$date])) {
$forecast[$date] = [
'date' => $date,
'temp_min' => $item['main']['temp_min'],
'temp_max' => $item['main']['temp_max'],
'description' => $item['weather'][0]['description'],
'icon' => $item['weather'][0]['icon'],
'items' => []
];
}
$forecast[$date]['temp_min'] = min(
$forecast[$date]['temp_min'],
$item['main']['temp_min']
);
$forecast[$date]['temp_max'] = max(
$forecast[$date]['temp_max'],
$item['main']['temp_max']
);
$forecast[$date]['items'][] = [
'time' => date('H:i', $item['dt']),
'temp' => round($item['main']['temp'], 1),
'description' => $item['weather'][0]['description']
];
}
$this->setCache($cacheKey, $forecast);
return $forecast;
}
/**
* API ์์ฒญ ์คํ
*/
private function makeRequest($url, $params) {
$fullUrl = $url . '?' . http_build_query($params);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $fullUrl,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_HTTPHEADER => [
'Accept: application/json'
]
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
throw new Exception("API ์์ฒญ ์คํจ: {$error}");
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception("API ์๋ฌ: HTTP {$httpCode}");
}
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception("JSON ํ์ฑ ์คํจ: " . json_last_error_msg());
}
return $data;
}
/**
* ์บ์ ์กฐํ
*/
private function getCache($key) {
$cacheFile = "{$this->cacheDir}/{$key}.json";
if (!file_exists($cacheFile)) {
return null;
}
$cacheAge = time() - filemtime($cacheFile);
if ($cacheAge > $this->cacheTime) {
unlink($cacheFile);
return null;
}
$content = file_get_contents($cacheFile);
return json_decode($content, true);
}
/**
* ์บ์ ์ ์ฅ
*/
private function setCache($key, $data) {
$cacheFile = "{$this->cacheDir}/{$key}.json";
file_put_contents($cacheFile, json_encode($data));
}
}
// ์ฌ์ฉ ์์
try {
$weather = new WeatherAPI('your_api_key_here');
// ํ์ฌ ๋ ์จ
$current = $weather->getCurrentWeather('Seoul');
echo "=== {$current['city']} ํ์ฌ ๋ ์จ ===\n";
echo "์จ๋: {$current['temperature']}ยฐC\n";
echo "์ฒด๊ฐ์จ๋: {$current['feels_like']}ยฐC\n";
echo "๋ ์จ: {$current['description']}\n";
echo "์ต๋: {$current['humidity']}%\n";
echo "ํ์: {$current['wind_speed']}m/s\n\n";
// 5์ผ ์๋ณด
$forecast = $weather->getForecast('Seoul');
echo "=== 5์ผ ์๋ณด ===\n";
foreach ($forecast as $day) {
echo "{$day['date']}: ";
echo "{$day['temp_min']}ยฐC ~ {$day['temp_max']}ยฐC ";
echo "({$day['description']})\n";
}
} catch (Exception $e) {
echo "์๋ฌ: " . $e->getMessage();
}
?>
โ ์บ์ฑ ๊ตฌํ: API ํธ์ถ ์ต์ํ๋ก ๋น์ฉ ์ ๊ฐ
โ ์๋ฌ ์ฒ๋ฆฌ: try-catch๋ก ์์ ํ ์คํ
โ ๋ฐ์ดํฐ ๊ฐ๊ณต: API ์๋ต์ ์ฌ์ฉํ๊ธฐ ์ฝ๊ฒ ๋ณํ
โ ํ๋ผ๋ฏธํฐ ๊ด๋ฆฌ: ์ ์ฐํ ์ต์ ์ฒ๋ฆฌ
์ด ํจํด์ ๋ค๋ฅธ API์๋ ๊ทธ๋๋ก ์ ์ฉํ ์ ์์ด! ๐ฏ
๐ก๏ธ API ํต์ ๋ณด์ ๋ฒ ์คํธ ํ๋ํฐ์ค
API๋ฅผ ๋ค๋ฃฐ ๋ ๋ณด์์ ์ ๋ง ์ค์ํด! ์ค์ ํ๋๋ก ํฐ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๊ฑฐ๋ . ์ค๋ฌด์์ ๊ผญ ์ง์ผ์ผ ํ ๋ณด์ ์์น๋ค์ ์์๋ณด์. ๐
1. ๐ API ํค ๊ด๋ฆฌ
์ ๋ ํ์ง ๋ง์์ผ ํ ๊ฒ:
โ ์ฝ๋์ ์ง์ ํ๋์ฝ๋ฉ
โ Git์ ์ปค๋ฐ
โ ํด๋ผ์ด์ธํธ ์ฌ์ด๋์ ๋
ธ์ถ
โ ๋ก๊ทธ์ ์ถ๋ ฅ
์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ:
<?php
// .env ํ์ผ ์ฌ์ฉ (Git์๋ .env.example๋ง ์ปค๋ฐ)
// .env ํ์ผ ๋ด์ฉ:
// API_KEY=your_secret_key_here
// API_SECRET=your_secret_here
// config.php
$dotenv = parse_ini_file('.env');
define('API_KEY', $dotenv['API_KEY']);
define('API_SECRET', $dotenv['API_SECRET']);
// ๋๋ ํ๊ฒฝ๋ณ์ ์ฌ์ฉ
$apiKey = getenv('API_KEY');
// ์ฌ์ฉํ ๋
$headers = [
'Authorization: Bearer ' . API_KEY
];
?>
2. ๐ HTTPS ์ฌ์ฉ ๊ฐ์
์ ๋ HTTP๋ก ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ฅผ ์ ์กํ์ง ๋ง! ํญ์ HTTPS๋ฅผ ์ฌ์ฉํด์ผ ํด.
<?php
// SSL ์ธ์ฆ์ ๊ฒ์ฆ ํ์ฑํ
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
// ํน์ CA ์ธ์ฆ์ ์ฌ์ฉ (๋ ์์ )
curl_setopt($ch, CURLOPT_CAINFO, '/path/to/cacert.pem');
// URL ๊ฒ์ฆ
function isSecureUrl($url) {
$parsed = parse_url($url);
return isset($parsed['scheme']) && $parsed['scheme'] === 'https';
}
if (!isSecureUrl($apiUrl)) {
throw new Exception('HTTPS URL๋ง ํ์ฉ๋ฉ๋๋ค');
}
?>
3. โฑ๏ธ Rate Limiting ๊ตฌํ
API ํธ์ถ ํ์๋ฅผ ์ ํํด์ ๋จ์ฉ์ ๋ฐฉ์งํ๊ณ ๋น์ฉ์ ์ ๊ฐํด์ผ ํด.
<?php
class RateLimiter {
private $cacheDir = './rate_limit';
private $maxRequests = 100; // ์๊ฐ๋น ์ต๋ ์์ฒญ ์
private $timeWindow = 3600; // 1์๊ฐ
public function __construct($maxRequests = 100, $timeWindow = 3600) {
$this->maxRequests = $maxRequests;
$this->timeWindow = $timeWindow;
if (!is_dir($this->cacheDir)) {
mkdir($this->cacheDir, 0755, true);
}
}
public function checkLimit($identifier) {
$file = "{$this->cacheDir}/" . md5($identifier) . ".json";
// ๊ธฐ๋ก ๋ก๋
if (file_exists($file)) {
$data = json_decode(file_get_contents($file), true);
} else {
$data = ['requests' => [], 'count' => 0];
}
// ์ค๋๋ ์์ฒญ ์ ๊ฑฐ
$now = time();
$data['requests'] = array_filter($data['requests'], function($timestamp) use ($now) {
return ($now - $timestamp) < $this->timeWindow;
});
$data['count'] = count($data['requests']);
// ์ ํ ํ์ธ
if ($data['count'] >= $this->maxRequests) {
$oldestRequest = min($data['requests']);
$resetTime = $oldestRequest + $this->timeWindow;
$waitSeconds = $resetTime - $now;
throw new Exception(
"Rate limit ์ด๊ณผ. {$waitSeconds}์ด ํ ์ฌ์๋ํ์ธ์."
);
}
// ์ ์์ฒญ ๊ธฐ๋ก
$data['requests'][] = $now;
$data['count']++;
file_put_contents($file, json_encode($data));
return [
'remaining' => $this->maxRequests - $data['count'],
'reset' => $now + $this->timeWindow
];
}
}
// ์ฌ์ฉ ์์
$limiter = new RateLimiter(100, 3600); // ์๊ฐ๋น 100ํ
try {
$status = $limiter->checkLimit('user_123');
echo "๋จ์ ์์ฒญ: {$status['remaining']}ํ\n";
// API ํธ์ถ ์งํ
$result = callAPI();
} catch (Exception $e) {
echo "์๋ฌ: " . $e->getMessage();
}
?>
4. ๐ญ ์ ๋ ฅ ๊ฒ์ฆ ๋ฐ ์๋ํ์ด์ง
์ฌ์ฉ์ ์ ๋ ฅ์ ์ ๋ ๋ฏฟ์ง ๋ง! ํญ์ ๊ฒ์ฆํ๊ณ ์ ์ ํด์ผ ํด.
<?php
class InputValidator {
/**
* URL ๊ฒ์ฆ
*/
public static function validateUrl($url) {
$url = filter_var($url, FILTER_VALIDATE_URL);
if ($url === false) {
throw new Exception('์ ํจํ์ง ์์ URL์
๋๋ค');
}
// HTTPS ๊ฐ์
if (parse_url($url, PHP_URL_SCHEME) !== 'https') {
throw new Exception('HTTPS URL๋ง ํ์ฉ๋ฉ๋๋ค');
}
// ํ์ดํธ๋ฆฌ์คํธ ๋๋ฉ์ธ ์ฒดํฌ
$allowedDomains = ['api.example.com', 'api.trusted.com'];
$host = parse_url($url, PHP_URL_HOST);
if (!in_array($host, $allowedDomains)) {
throw new Exception('ํ์ฉ๋์ง ์์ ๋๋ฉ์ธ์
๋๋ค');
}
return $url;
}
/**
* JSON ๋ฐ์ดํฐ ๊ฒ์ฆ
*/
public static function validateJson($json, $schema) {
$data = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception('์ ํจํ์ง ์์ JSON์
๋๋ค');
}
// ์คํค๋ง ๊ฒ์ฆ
foreach ($schema as $field => $rules) {
if ($rules['required'] && !isset($data[$field])) {
throw new Exception("{$field} ํ๋๋ ํ์์
๋๋ค");
}
if (isset($data[$field])) {
$value = $data[$field];
// ํ์
์ฒดํฌ
if (isset($rules['type'])) {
$actualType = gettype($value);
if ($actualType !== $rules['type']) {
throw new Exception(
"{$field}๋ {$rules['type']} ํ์
์ด์ด์ผ ํฉ๋๋ค"
);
}
}
// ๊ธธ์ด ์ฒดํฌ
if (isset($rules['maxLength']) && is_string($value)) {
if (strlen($value) > $rules['maxLength']) {
throw new Exception(
"{$field}๋ ์ต๋ {$rules['maxLength']}์์
๋๋ค"
);
}
}
// ํจํด ์ฒดํฌ
if (isset($rules['pattern']) && is_string($value)) {
if (!preg_match($rules['pattern'], $value)) {
throw new Exception("{$field} ํ์์ด ์ฌ๋ฐ๋ฅด์ง ์์ต๋๋ค");
}
}
}
}
return $data;
}
/**
* SQL ์ธ์ ์
๋ฐฉ์ง
*/
public static function sanitizeForDb($value, $pdo) {
if (is_array($value)) {
return array_map(function($v) use ($pdo) {
return self::sanitizeForDb($v, $pdo);
}, $value);
}
return $pdo->quote($value);
}
}
// ์ฌ์ฉ ์์
$schema = [
'username' => [
'required' => true,
'type' => 'string',
'maxLength' => 50,
'pattern' => '/^[a-zA-Z0-9_]+$/'
],
'email' => [
'required' => true,
'type' => 'string',
'pattern' => '/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/'
],
'age' => [
'required' => false,
'type' => 'integer'
]
];
try {
$validData = InputValidator::validateJson($jsonInput, $schema);
// ๊ฒ์ฆ๋ ๋ฐ์ดํฐ ์ฌ์ฉ
} catch (Exception $e) {
echo "๊ฒ์ฆ ์คํจ: " . $e->getMessage();
}
?>
- โ๏ธ API ํค๋ฅผ ํ๊ฒฝ๋ณ์๋ ๋ณ๋ ์ค์ ํ์ผ๋ก ๊ด๋ฆฌ
- โ๏ธ HTTPS๋ง ์ฌ์ฉํ๊ณ SSL ์ธ์ฆ์ ๊ฒ์ฆ
- โ๏ธ Rate limiting ๊ตฌํ
- โ๏ธ ์ ๋ ฅ ๋ฐ์ดํฐ ๊ฒ์ฆ ๋ฐ ์๋ํ์ด์ง
- โ๏ธ ์๋ฌ ๋ฉ์์ง์ ๋ฏผ๊ฐํ ์ ๋ณด ํฌํจ ๊ธ์ง
- โ๏ธ ๋ก๊ทธ์ API ํค๋ ๋น๋ฐ๋ฒํธ ๊ธฐ๋ก ๊ธ์ง
- โ๏ธ ํ์์์ ์ค์ ์ผ๋ก ๋ฌดํ ๋๊ธฐ ๋ฐฉ์ง
- โ๏ธ CORS ์ค์ (์น API์ธ ๊ฒฝ์ฐ)
- โ๏ธ ์ ๊ธฐ์ ์ธ ๋ณด์ ์ ๋ฐ์ดํธ
- โ๏ธ API ๋ฒ์ ๊ด๋ฆฌ
๐ฏ ์ค์ ํ: API ๋๋ฒ๊น ๊ณผ ํ ์คํธ
API ๊ฐ๋ฐํ๋ค ๋ณด๋ฉด ๋ฌธ์ ๊ฐ ์๊ธฐ๊ธฐ ๋ง๋ จ์ด์ผ. ํจ๊ณผ์ ์ผ๋ก ๋๋ฒ๊น ํ๊ณ ํ ์คํธํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์! ๐
๐ ๋๋ฒ๊น ๋๊ตฌ ํ์ฉ
<?php
function debugCurl($ch) {
// ์์ธ ์ ๋ณด ์ถ๋ ฅ ํ์ฑํ
curl_setopt($ch, CURLOPT_VERBOSE, true);
// ์ถ๋ ฅ์ ํ์ผ๋ก ์ ์ฅ
$verbose = fopen('php://temp', 'w+');
curl_setopt($ch, CURLOPT_STDERR, $verbose);
$response = curl_exec($ch);
// ๋๋ฒ๊ทธ ์ ๋ณด ์ฝ๊ธฐ
rewind($verbose);
$verboseLog = stream_get_contents($verbose);
echo "=== cURL ๋๋ฒ๊ทธ ์ ๋ณด ===\n";
echo $verboseLog;
// ์์ฒญ ์ ๋ณด
$info = curl_getinfo($ch);
echo "\n=== ์์ฒญ ์ ๋ณด ===\n";
echo "URL: {$info['url']}\n";
echo "HTTP ์ฝ๋: {$info['http_code']}\n";
echo "์ด ์๊ฐ: {$info['total_time']}์ด\n";
echo "๋ค์ด๋ก๋ ํฌ๊ธฐ: {$info['size_download']}๋ฐ์ดํธ\n";
fclose($verbose);
return $response;
}
?>
<?php
class APILogger {
private $logFile;
public function __construct($logFile = './logs/api.log') {
$this->logFile = $logFile;
$dir = dirname($logFile);
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
}
public function log($level, $message, $context = []) {
$timestamp = date('Y-m-d H:i:s');
$contextStr = !empty($context) ? json_encode($context) : '';
$logEntry = "[{$timestamp}] [{$level}] {$message}";
if ($contextStr) {
$logEntry .= " | Context: {$contextStr}";
}
$logEntry .= "\n";
file_put_contents($this->logFile, $logEntry, FILE_APPEND);
}
public function logRequest($url, $method, $data = null) {
$this->log('INFO', "API ์์ฒญ", [
'url' => $url,
'method' => $method,
'data' => $data
]);
}
public function logResponse($statusCode, $response, $duration) {
$this->log('INFO', "API ์๋ต", [
'status' => $statusCode,
'duration' => $duration . 'ms',
'response_size' => strlen($response) . ' bytes'
]);
}
public function logError($error, $context = []) {
$this->log('ERROR', $error, $context);
}
}
// ์ฌ์ฉ ์์
$logger = new APILogger();
$startTime = microtime(true);
$logger->logRequest($url, 'GET', $params);
try {
$response = curl_exec($ch);
$duration = round((microtime(true) - $startTime) * 1000, 2);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$logger->logResponse($httpCode, $response, $duration);
} catch (Exception $e) {
$logger->logError($e->getMessage(), [
'url' => $url,
'trace' => $e->getTraceAsString()
]);
}
?>
๐งช API ํ ์คํธ ์๋ํ
<?php
use PHPUnit\Framework\TestCase;
class WeatherAPITest extends TestCase {
private $api;
protected function setUp(): void {
$this->api = new WeatherAPI('test_api_key');
}
public function testGetCurrentWeather() {
$result = $this->api->getCurrentWeather('Seoul');
// ํ์ ํ๋ ํ์ธ
$this->assertArrayHasKey('city', $result);
$this->assertArrayHasKey('temperature', $result);
$this->assertArrayHasKey('description', $result);
// ๋ฐ์ดํฐ ํ์
ํ์ธ
$this->assertIsString($result['city']);
$this->assertIsFloat($result['temperature']);
// ๊ฐ ๋ฒ์ ํ์ธ
$this->assertGreaterThanOrEqual(-50, $result['temperature']);
$this->assertLessThanOrEqual(60, $result['temperature']);
}
public function testInvalidCity() {
$this->expectException(Exception::class);
$this->api->getCurrentWeather('InvalidCityName12345');
}
public function testCaching() {
// ์ฒซ ๋ฒ์งธ ํธ์ถ
$start1 = microtime(true);
$result1 = $this->api->getCurrentWeather('Seoul');
$time1 = microtime(true) - $start1;
// ๋ ๋ฒ์งธ ํธ์ถ (์บ์์์)
$start2 = microtime(true);
$result2 = $this->api->getCurrentWeather('Seoul');
$time2 = microtime(true) - $start2;
// ์บ์๊ฐ ๋ ๋นจ๋ผ์ผ ํจ
$this->assertLessThan($time1, $time2);
// ๊ฒฐ๊ณผ๋ ๋์ผํด์ผ ํจ
$this->assertEquals($result1, $result2);
}
}
?>
2. var_dump() ๋์ print_r(): ๋ฐฐ์ด ๊ตฌ์กฐ ๋ณด๊ธฐ ์ฌ์
3. json_encode($data, JSON_PRETTY_PRINT): JSON ์์๊ฒ ์ถ๋ ฅ
4. error_log(): ํ๋ก๋์ ์์ ์์ ํ ๋๋ฒ๊น
5. Xdebug: ๋จ๊ณ๋ณ ๋๋ฒ๊น ์ต๊ฐ์
6. Charles Proxy: HTTP ํธ๋ํฝ ๋ชจ๋ํฐ๋ง
ํนํ ์ฌ๋ฅ๋ท์์ ํ๋ฆฌ๋์๋ก ํ๋ก์ ํธ๋ฅผ ์งํํ ๋, ํด๋ผ์ด์ธํธ์๊ฒ API ์ฐ๋ ์ํฉ์ ์ค์๊ฐ์ผ๋ก ๋ณด์ฌ์ฃผ๋ฉด ์ ๋ขฐ๋๊ฐ ์ฌ๋ผ๊ฐ! ๐
๐ ์ฑ๋ฅ ์ต์ ํ ์ ๋ต
API ํต์ ์ ๋๋ฆด ์ ์์ด. ์ฌ์ฉ์ ๊ฒฝํ์ ํด์น์ง ์์ผ๋ ค๋ฉด ์ต์ ํ๊ฐ ํ์์ผ! โก
1. ๐๏ธ ์บ์ฑ ์ ๋ต
<?php
class CacheManager {
private $memcached;
private $fileCache;
public function __construct() {
// Memcached ์ฐ๊ฒฐ
if (class_exists('Memcached')) {
$this->memcached = new Memcached();
$this->memcached->addServer('localhost', 11211);
}
$this->fileCache = './cache';
if (!is_dir($this->fileCache)) {
mkdir($this->fileCache, 0755, true);
}
}
public function get($key) {
// 1๋จ๊ณ: ๋ฉ๋ชจ๋ฆฌ ์บ์ (๊ฐ์ฅ ๋น ๋ฆ)
if ($this->memcached) {
$value = $this->memcached->get($key);
if ($value !== false) {
return $value;
}
}
// 2๋จ๊ณ: ํ์ผ ์บ์
$file = "{$this->fileCache}/" . md5($key) . ".cache";
if (file_exists($file)) {
$data = unserialize(file_get_contents($file));
if ($data['expire'] > time()) {
// ๋ฉ๋ชจ๋ฆฌ ์บ์์๋ ์ ์ฅ
if ($this->memcached) {
$ttl = $data['expire'] - time();
$this->memcached->set($key, $data['value'], $ttl);
}
return $data['value'];
} else {
unlink($file);
}
}
return null;
}
public function set($key, $value, $ttl = 3600) {
// ๋ฉ๋ชจ๋ฆฌ ์บ์
if ($this->memcached) {
$this->memcached->set($key, $value, $ttl);
}
// ํ์ผ ์บ์
$file = "{$this->fileCache}/" . md5($key) . ".cache";
$data = [
'value' => $value,
'expire' => time() + $ttl
];
file_put_contents($file, serialize($data));
}
public function delete($key) {
if ($this->memcached) {
$this->memcached->delete($key);
}
$file = "{$this->fileCache}/" . md5($key) . ".cache";
if (file_exists($file)) {
unlink($file);
}
}
}
?>
2. โก ๋น๋๊ธฐ ์ฒ๋ฆฌ
<?php
class ParallelAPI {
/**
* ์ฌ๋ฌ API๋ฅผ ๋์์ ํธ์ถ
*/
public function multiRequest($requests) {
$mh = curl_multi_init();
$handles = [];
// ๊ฐ ์์ฒญ์ ๋ํ cURL ํธ๋ค ์์ฑ
foreach ($requests as $key => $request) {
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $request['url'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => $request['headers'] ?? []
]);
if (isset($request['method']) && $request['method'] === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request['data'] ?? '');
}
curl_multi_add_handle($mh, $ch);
$handles[$key] = $ch;
}
// ๋ชจ๋ ์์ฒญ ์คํ
$running = null;
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh);
} while ($running > 0);
// ๊ฒฐ๊ณผ ์์ง
$results = [];
foreach ($handles as $key => $ch) {
$results[$key] = [
'response' => curl_multi_getcontent($ch),
'info' => curl_getinfo($ch),
'error' => curl_error($ch)
];
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
curl_multi_close($mh);
return $results;
}
}
// ์ฌ์ฉ ์์: ์ฌ๋ฌ API๋ฅผ ๋์์ ํธ์ถ
$parallel = new ParallelAPI();
$requests = [
'weather' => [
'url' => 'https://api.weather.com/current?city=Seoul',
'headers' => ['X-API-Key: key1']
],
'news' => [
'url' => 'https://api.news.com/latest',
'headers' => ['X-API-Key: key2']
],
'stocks' => [
'url' => 'https://api.stocks.com/prices',
'headers' => ['X-API-Key: key3']
]
];
$startTime = microtime(true);
$results = $parallel->multiRequest($requests);
$duration = microtime(true) - $startTime;
echo "3๊ฐ API ๋์ ํธ์ถ ์๋ฃ: " . round($duration, 2) . "์ด\n";
foreach ($results as $key => $result) {
if (empty($result['error'])) {
$data = json_decode($result['response'], true);
echo "{$key}: ์ฑ๊ณต (HTTP {$result['info']['http_code']})\n";
} else {
echo "{$key}: ์คํจ - {$result['error']}\n";
}
}
?>
3. ๐ฏ ์กฐ๊ฑด๋ถ ์์ฒญ (Conditional Requests)
<?php
class ConditionalAPI {
private $cacheDir = './http_cache';
public function __construct() {
if (!is_dir($this->cacheDir)) {
mkdir($this->cacheDir, 0755, true);
}
}
public function request($url) {
$cacheKey = md5($url);
$cacheFile = "{$this->cacheDir}/{$cacheKey}.json";
$metaFile = "{$this->cacheDir}/{$cacheKey}.meta";
$headers = [];
// ์ด์ ์์ฒญ์ ๋ฉํ๋ฐ์ดํฐ ๋ก๋
if (file_exists($metaFile)) {
$meta = json_decode(file_get_contents($metaFile), true);
if (isset($meta['etag'])) {
$headers[] = "If-None-Match: {$meta['etag']}";
}
if (isset($meta['last_modified'])) {
$headers[] = "If-Modified-Since: {$meta['last_modified']}";
}
}
// API ์์ฒญ
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_HTTPHEADER => $headers
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
curl_close($ch);
// ํค๋์ ๋ณธ๋ฌธ ๋ถ๋ฆฌ
$responseHeaders = substr($response, 0, $headerSize);
$responseBody = substr($response, $headerSize);
// 304 Not Modified - ์บ์ ์ฌ์ฉ
if ($httpCode === 304) {
if (file_exists($cacheFile)) {
return json_decode(file_get_contents($cacheFile), true);
}
}
// 200 OK - ์ ๋ฐ์ดํฐ
if ($httpCode === 200) {
// ETag ์ถ์ถ
if (preg_match('/ETag: (.+)/i', $responseHeaders, $matches)) {
$etag = trim($matches[1]);
}
// Last-Modified ์ถ์ถ
if (preg_match('/Last-Modified: (.+)/i', $responseHeaders, $matches)) {
$lastModified = trim($matches[1]);
}
// ๋ฉํ๋ฐ์ดํฐ ์ ์ฅ
$meta = [
'etag' => $etag ?? null,
'last_modified' => $lastModified ?? null,
'cached_at' => time()
];
file_put_contents($metaFile, json_encode($meta));
// ์๋ต ์บ์
file_put_contents($cacheFile, $responseBody);
return json_decode($responseBody, true);
}
throw new Exception("HTTP ์๋ฌ: {$httpCode}");
}
}
// ์ฌ์ฉ ์์
$api = new ConditionalAPI();
// ์ฒซ ๋ฒ์งธ ์์ฒญ: ์ ์ฒด ๋ฐ์ดํฐ ๋ค์ด๋ก๋
$data1 = $api->request('https://api.example.com/data');
echo "์ฒซ ์์ฒญ: ์ ์ฒด ๋ค์ด๋ก๋\n";
// ๋ ๋ฒ์งธ ์์ฒญ: ๋ณ๊ฒฝ์ฌํญ ์์ผ๋ฉด 304 ์๋ต (๋์ญํญ ์ ์ฝ!)
$data2 = $api->request('https://api.example.com/data');
echo "๋ ๋ฒ์งธ ์์ฒญ: ์บ์ ์ฌ์ฉ (๋์ญํญ ์ ์ฝ!)\n";
?>
- โ๏ธ ์บ์ฑ: ๋ฐ๋ณต ์์ฒญ ์ต์ํ (๋ฉ๋ชจ๋ฆฌ > ํ์ผ > DB)
- โ๏ธ ๋ณ๋ ฌ ์ฒ๋ฆฌ: ์ฌ๋ฌ API ๋์ ํธ์ถ
- โ๏ธ ์กฐ๊ฑด๋ถ ์์ฒญ: ETag, Last-Modified ํ์ฉ
- โ๏ธ ์์ถ: gzip ์ธ์ฝ๋ฉ ํ์ฑํ
- โ๏ธ ํ์์์: ์ ์ ํ ํ์์์ ์ค์
- โ๏ธ ์ฐ๊ฒฐ ์ฌ์ฌ์ฉ: Keep-Alive ํ์ฑํ
- โ๏ธ ํ์ด์ง๋ค์ด์ : ๋๋ ๋ฐ์ดํฐ๋ ๋๋ ์ ์์ฒญ
- โ๏ธ ๋น๋๊ธฐ ์ฒ๋ฆฌ: ๋ฐฑ๊ทธ๋ผ์ด๋ ์์ ํ์ฉ
- โ๏ธ CDN: ์ ์ ๋ฆฌ์์ค๋ CDN ์ฌ์ฉ
- โ๏ธ ๋ชจ๋ํฐ๋ง: ์ฑ๋ฅ ์งํ ์ถ์
๐ ์ค์ ์์ : ์์ ํ API ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
์ง๊ธ๊น์ง ๋ฐฐ์ด ๋ชจ๋ ๊ฑธ ์ข ํฉํด์ ์ค๋ฌด์์ ๋ฐ๋ก ์ธ ์ ์๋ ์์ ํ API ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ค์ด๋ณด์! ๐
<?php
/**
* ๋ฒ์ฉ API ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
*
* ๊ธฐ๋ฅ:
* - GET, POST, PUT, DELETE ์ง์
* - ์๋ ์ฌ์๋
* - ์บ์ฑ
* - Rate limiting
* - ๋ก๊น
* - ์๋ฌ ์ฒ๋ฆฌ
*/
class APIClient {
private $baseUrl;
private $headers = [];
private $timeout = 30;
private $retryCount = 3;
private $retryDelay = 1000; // ๋ฐ๋ฆฌ์ด
private $cache;
private $logger;
private $rateLimiter;
public function __construct($baseUrl, $config = []) {
$this->baseUrl = rtrim($baseUrl, '/');
// ์ค์ ์ ์ฉ
if (isset($config['timeout'])) {
$this->timeout = $config['timeout'];
}
if (isset($config['retry_count'])) {
$this->retryCount = $config['retry_count'];
}
if (isset($config['retry_delay'])) {
$this->retryDelay = $config['retry_delay'];
}
// ์บ์ ์ด๊ธฐํ
$this->cache = new CacheManager();
// ๋ก๊ฑฐ ์ด๊ธฐํ
$this->logger = new APILogger();
// Rate limiter ์ด๊ธฐํ
if (isset($config['rate_limit'])) {
$this->rateLimiter = new RateLimiter(
$config['rate_limit']['max_requests'],
$config['rate_limit']['time_window']
);
}
}
/**
* ๊ธฐ๋ณธ ํค๋ ์ค์
*/
public function setHeaders($headers) {
$this->headers = array_merge($this->headers, $headers);
return $this;
}
/**
* ์ธ์ฆ ํ ํฐ ์ค์
*/
public function setAuthToken($token, $type = 'Bearer') {
$this->headers['Authorization'] = "{$type} {$token}";
return $this;
}
/**
* API Key ์ค์
*/
public function setApiKey($key, $headerName = 'X-API-Key') {
$this->headers[$headerName] = $key;
return $this;
}
/**
* GET ์์ฒญ
*/
public function get($endpoint, $params = [], $options = []) {
if (!empty($params)) {
$endpoint .= '?' . http_build_query($params);
}
return $this->request('GET', $endpoint, null, $options);
}
/**
* POST ์์ฒญ
*/
public function post($endpoint, $data = [], $options = []) {
return $this->request('POST', $endpoint, $data, $options);
}
/**
* PUT ์์ฒญ
*/
public function put($endpoint, $data = [], $options = []) {
return $this->request('PUT', $endpoint, $data, $options);
}
/**
* DELETE ์์ฒญ
*/
public function delete($endpoint, $options = []) {
return $this->request('DELETE', $endpoint, null, $options);
}
/**
* ์ค์ HTTP ์์ฒญ ์คํ
*/
private function request($method, $endpoint, $data = null, $options = []) {
$url = $this->baseUrl . '/' . ltrim($endpoint, '/');
// Rate limiting ์ฒดํฌ
if ($this->rateLimiter) {
try {
$this->rateLimiter->checkLimit($this->baseUrl);
} catch (Exception $e) {
$this->logger->logError($e->getMessage());
throw $e;
}
}
// ์บ์ ํ์ธ (GET ์์ฒญ๋ง)
if ($method === 'GET' && !isset($options['no_cache'])) {
$cacheKey = md5($url);
$cached = $this->cache->get($cacheKey);
if ($cached !== null) {
$this->logger->log('INFO', "์บ์ ํํธ: {$url}");
return $cached;
}
}
// ์ฌ์๋ ๋ก์ง
$attempt = 0;
$lastError = null;
while ($attempt < $this->retryCount) {
try {
$attempt++;
$this->logger->logRequest($url, $method, $data);
$startTime = microtime(true);
$response = $this->executeRequest($method, $url, $data, $options);
$duration = round((microtime(true) - $startTime) * 1000, 2);
$this->logger->logResponse(200, $response, $duration);
// ์บ์ ์ ์ฅ (GET ์์ฒญ๋ง)
if ($method === 'GET' && !isset($options['no_cache'])) {
$cacheTTL = $options['cache_ttl'] ?? 3600;
$this->cache->set($cacheKey, $response, $cacheTTL);
}
return $response;
} catch (Exception $e) {
$lastError = $e;
$this->logger->logError(
"์์ฒญ ์คํจ (์๋ {$attempt}/{$this->retryCount}): " . $e->getMessage(),
['url' => $url, 'method' => $method]
);
// ๋ง์ง๋ง ์๋๊ฐ ์๋๋ฉด ๋๊ธฐ ํ ์ฌ์๋
if ($attempt < $this->retryCount) {
usleep($this->retryDelay * 1000);
}
}
}
// ๋ชจ๋ ์ฌ์๋ ์คํจ
throw new Exception(
"API ์์ฒญ ์คํจ ({$this->retryCount}ํ ์๋): " . $lastError->getMessage()
);
}
/**
* cURL๋ก ์ค์ ์์ฒญ ์คํ
*/
private function executeRequest($method, $url, $data, $options) {
$ch = curl_init();
// ๊ธฐ๋ณธ ์ต์
$curlOptions = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_FOLLOWLOCATION => true
];
// ํค๋ ์ค์
$headers = [];
foreach ($this->headers as $key => $value) {
$headers[] = "{$key}: {$value}";
}
if (!empty($headers)) {
$curlOptions[CURLOPT_HTTPHEADER] = $headers;
}
// ๋ฉ์๋๋ณ ์ค์
switch ($method) {
case 'POST':
$curlOptions[CURLOPT_POST] = true;
if ($data !== null) {
$curlOptions[CURLOPT_POSTFIELDS] = json_encode($data);
$headers[] = 'Content-Type: application/json';
}
break;
case 'PUT':
$curlOptions[CURLOPT_CUSTOMREQUEST] = 'PUT';
if ($data !== null) {
$curlOptions[CURLOPT_POSTFIELDS] = json_encode($data);
$headers[] = 'Content-Type: application/json';
}
break;
case 'DELETE':
$curlOptions[CURLOPT_CUSTOMREQUEST] = 'DELETE';
break;
}
curl_setopt_array($ch, $curlOptions);
$response = curl_exec($ch);
if (curl_errno($ch)) {
$error = curl_error($ch);
curl_close($ch);
throw new Exception("cURL ์๋ฌ: {$error}");
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// HTTP ์ํ ์ฝ๋ ์ฒดํฌ
if ($httpCode < 200 || $httpCode >= 300) {
throw new Exception("HTTP ์๋ฌ {$httpCode}: {$response}");
}
// JSON ํ์ฑ
$decoded = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception("JSON ํ์ฑ ์คํจ: " . json_last_error_msg());
}
return $decoded;
}
/**
* ์บ์ ์ด๊ธฐํ
*/
public function clearCache() {
// ๊ตฌํ ํ์
}
}
// ============================================
// ์ฌ์ฉ ์์
// ============================================
// 1. ๊ธฐ๋ณธ ์ฌ์ฉ
$client = new APIClient('https://api.example.com', [
'timeout' => 30,
'retry_count' => 3,
'rate_limit' => [
'max_requests' => 100,
'time_window' => 3600
]
]);
// ์ธ์ฆ ์ค์
$client->setAuthToken('your_token_here');
// GET ์์ฒญ
try {
$users = $client->get('/users', ['page' => 1, 'limit' => 10]);
print_r($users);
} catch (Exception $e) {
echo "์๋ฌ: " . $e->getMessage();
}
// POST ์์ฒญ
try {
$newUser = $client->post('/users', [
'name' => 'ํ๊ธธ๋',
'email' => 'hong@example.com'
]);
echo "์ฌ์ฉ์ ์์ฑ ์ฑ๊ณต: {$newUser['id']}\n";
} catch (Exception $e) {
echo "์๋ฌ: " . $e->getMessage();
}
// PUT ์์ฒญ
try {
$updated = $client->put('/users/123', [
'name' => 'ํ๊ธธ๋2'
]);
echo "์ฌ์ฉ์ ์์ ์ฑ๊ณต\n";
} catch (Exception $e) {
echo "์๋ฌ: " . $e->getMessage();
}
// DELETE ์์ฒญ
try {
$client->delete('/users/123');
echo "์ฌ์ฉ์ ์ญ์ ์ฑ๊ณต\n";
} catch (Exception $e) {
echo "์๋ฌ: " . $e->getMessage();
}
// 2. ์ฌ๋ฌ API ๋์ ํธ์ถ
$weatherClient = new APIClient('https://api.weather.com');
$newsClient = new APIClient('https://api.news.com');
$weatherClient->setApiKey('weather_key');
$newsClient->setApiKey('news_key');
try {
$weather = $weatherClient->get('/current', ['city' => 'Seoul']);
$news = $newsClient->get('/latest', ['category' => 'tech']);
echo "๋ ์จ: {$weather['temp']}ยฐC\n";
echo "๋ด์ค: {$news['articles'][0]['title']}\n";
} catch (Exception $e) {
echo "์๋ฌ: " . $e->getMessage();
}
?>
โ ์์ ์ฑ: ์๋ ์ฌ์๋, ์๋ฌ ์ฒ๋ฆฌ
โ ์ฑ๋ฅ: ์บ์ฑ, Rate limiting
โ ์ ์ง๋ณด์: ์ค์ํ๋ ๋ก์ง ๊ด๋ฆฌ
โ ํ์ฅ์ฑ: ์ฝ๊ฒ ๊ธฐ๋ฅ ์ถ๊ฐ ๊ฐ๋ฅ
์ค๋ฌด์์๋ ์ด๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ค์ด๋๊ณ ํ์๋ค๊ณผ ๊ณต์ ํ๋ฉด ๊ฐ๋ฐ ์๋๊ฐ ์์ฒญ ๋นจ๋ผ์ ธ! ๐ช
๐ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์! ์ฌ๊ธฐ๊น์ง ์ ๋ง ๊ธด ์ฌ์ ์ด์์ด. PHP์์ XML๊ณผ JSON์ ํ์ฑํ๊ณ , cURL๋ก API ํต์ ํ๋ ๋ฐฉ๋ฒ์ ๋ชจ๋ ๋ฐฐ์ ์ด. ๐
์ด์ ๋๋ ์ธ๋ถ API๋ฅผ ์์ ์์ฌ๋ก ๋ค๋ฃฐ ์ ์๋ ๊ฐ๋ฐ์๊ฐ ๋์ด! ๋ ์จ ์ ๋ณด๋ , ๊ฒฐ์ ์์คํ
์ด๋ , ์์
๋ก๊ทธ์ธ์ด๋ ๋ญ๋ ์ง ์ฐ๋ํ ์ ์์ง. ๐ช
ํนํ ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ํ๋ฆฌ๋์๋ก ํ๋ํ ๋, ์ด๋ฐ API ํตํฉ ๋ฅ๋ ฅ์ ์ ๋ง ํฐ ๋ฌด๊ธฐ๊ฐ ๋ผ. ํด๋ผ์ด์ธํธ๋ค์ด ๊ฐ์ฅ ๋ง์ด ์์ฒญํ๋ ๊ธฐ๋ฅ ์ค ํ๋๊ฑฐ๋ ! ๐
๐ ํต์ฌ ์์ฝ
1. ๋ฐ์ดํฐ ํ์
โข XML: ๊ตฌ์กฐ์ ์ด๊ณ ์๊ฒฉ, ๋ ๊ฑฐ์ ์์คํ
์ ๋ง์ด ์ฌ์ฉ
โข JSON: ๊ฐ๋ณ๊ณ ๋น ๋ฆ, ํ๋์ ์ธ API์ ํ์ค
2. ํ์ฑ ๋ฐฉ๋ฒ
โข XML: SimpleXML (๊ฐ๋จ), DOMDocument (๊ฐ๋ ฅ), XMLReader (๋์ฉ๋)
โข JSON: json_decode(), json_encode() ๋ ๊ฐ๋ฉด ์ถฉ๋ถ!
3. API ํต์
โข cURL: ๊ฐ๋ ฅํ๊ณ ์ ์ฐ, ํ๋ก๋์
ํ๊ฒฝ ์ถ์ฒ
โข file_get_contents(): ๊ฐ๋จํ ์์ฒญ์ ์ ํฉ
4. ๋ณด์
โข API ํค๋ ํ๊ฒฝ๋ณ์๋ก ๊ด๋ฆฌ
โข HTTPS ํ์, SSL ์ธ์ฆ์ ๊ฒ์ฆ
โข Rate limiting์ผ๋ก ๋จ์ฉ ๋ฐฉ์ง
โข ์
๋ ฅ ๊ฒ์ฆ ์ฒ ์ ํ
5. ์ฑ๋ฅ
โข ์บ์ฑ์ผ๋ก ๋ฐ๋ณต ์์ฒญ ์ต์ํ
โข ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ก ์๋ ํฅ์
โข ์กฐ๊ฑด๋ถ ์์ฒญ์ผ๋ก ๋์ญํญ ์ ์ฝ
โข ์ ์ ํ ํ์์์ ์ค์
โข OpenWeatherMap, GitHub API, ๊ณต๊ณต๋ฐ์ดํฐํฌํธ ๋ฑ
2. ์ฌํ: GraphQL, WebSocket ๊ฐ์ ์ต์ ๊ธฐ์ ๋ฐฐ์ฐ๊ธฐ
3. ์ต์ ํ: ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง ๋๊ตฌ ํ์ฉํ๊ธฐ
4. ๋ณด์: OAuth 2.0, JWT ๊ฐ์ ์ธ์ฆ ๋ฐฉ์ ๋ง์คํฐํ๊ธฐ
5. ๊ณต์ : ๋ธ๋ก๊ทธ๋ GitHub์ ์ฝ๋ ๊ณต์ ํ๊ธฐ
API ํต์ ์ ํ๋ ์น ๊ฐ๋ฐ์ ํต์ฌ์ด์ผ. ์ด ๊ธฐ์ ์ ๋ง์คํฐํ๋ฉด ์ ๋ง ๋ง์ ๊ฐ๋ฅ์ฑ์ด ์ด๋ ค. ํผ์์ ๋ชจ๋ ๊ฑธ ๋ง๋ค ํ์ ์์ด, ์ธ์์ ์๋ง์ ์๋น์ค๋ค์ ์กฐํฉํด์ ๋ฉ์ง ํ๋ก์ ํธ๋ฅผ ๋ง๋ค ์ ์๊ฑฐ๋ ! ๐
๊ณ์ ์ฐ์ตํ๊ณ , ์ค์ํ๊ณ , ๋ฐฐ์๋๊ฐ๋ฉด์ ์ฑ์ฅํ๊ธธ ๋ฐ๋ผ! ํ์ดํ
! ๐ชโจ
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.