개발에 입문할 때 처음 배웠던 프로그래밍 언어가 Javascript였기 때문에 백엔드 작업이 필요할 때 같은 문법을 사용하는 Node 환경을 사용해 왔다.
그렇기에 Java, PHP, Ruby 같은 다른 언어를 공부하거나 볼 수 있는 기회가 없었다. 하지만 현재 회사에서는 PHP를 사용했기 때문에 Flow chart 확인 및 유지보수 그리고 추후 리펙터링과 Node.js로 컨버팅 하기 위해 느꼈던 점을 작성하고자 한다.
1. 호출 방식
PHP
const registerUser = () => {
axios.post('http://localhost/register.php', registerInfo)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
};
const changeNickname = () => {
axios.post('http://localhost/changeNickname.php', nicknameChangeInfo)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
};
일반적으로 위와 같은 프론트 코드가 있을 때 PHP 파일을 직접 호출하는 방식으로 작성한다.
// register.php
<?php
// 데이터베이스 연결
$servername = "localhost";
$username = "사용자명";
$password = "비밀번호";
$dbname = "데이터베이스명";
$conn = new mysqli($servername, $username, $password, $dbname);
// 회원가입 양식에서 전송된 데이터 가져오기
$username = $_POST['username'];
$email = $_POST['email'];
$password = $_POST['password'];
// 데이터베이스에 회원 정보 추가
$sql = "INSERT INTO users (username, email, password) VALUES ('$username', '$email', '$password')";
if ($conn->query($sql) === TRUE) {
echo "회원가입이 완료되었습니다.";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
?>
Node.js
const registerUser = () => {
axios.post('http://localhost:3000/register', registerInfo)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
};
const changeNickname = () => {
axios.post('http://localhost:3000/changeNickname', changeNicknameInfo)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
};
// index.js
const express = require('express');
const mysql = require('mysql');
const bcrypt = require('bcrypt');
const app = express();
app.use(express.json());
const db = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'testdb'
});
db.connect((err) => {
if (err) throw err;
console.log('Connected to the database.');
});
// 회원 가입
app.post('/register', (req, res) => {
const { id, password, nickname } = req.body;
bcrypt.hash(password, 10, (err, hash) => {
if (err) throw err;
db.query('INSERT INTO users SET ?', { id: id, password: hash, nickname: nickname }, (err, result) => {
if (err) throw err;
res.send('User registered successfully.');
});
});
});
// 닉네임 변경
app.post('/changeNickname', (req, res) => {
const { id, newNickname } = req.body;
db.query('UPDATE users SET nickname = ? WHERE id = ?', [newNickname, id], (err, result) => {
if (err) throw err;
res.send('Nickname changed successfully.');
});
});
const port = 3000;
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
Node의 경우 파일명을 직접 호출하지 않고 Router 방식으로 호출을 하게 된다.
위 코드는 한 번에 확인하기 위한 예시 코드이고 실제론 index -> router -> controller 순서로 진행이 되며 각각 해당하는 코드를 나누어 관리하게 된다.
하지만 PHP도 POST의 action 필드에 원하는 요청을 담아 위 Node 예시처럼 하나의 파일에서 다른 요청을 처리할 수도 있다.
// POST 데이터 처리
$postData = json_decode(file_get_contents('php://input'), true);
if (isset($postData['action'])) {
switch ($postData['action']) {
case 'register':
// 회원 가입 로직
$id = $postData['id'];
$password = $postData['password']; // 비밀번호는 해싱 처리 필요
$nickname = $postData['nickname'];
$sql = "INSERT INTO users (id, password, nickname) VALUES ('$id', '$password', '$nickname')";
if ($conn->query($sql) === TRUE) {
echo "New record created successfully";
} else {
echo "Error: " . $sql . "<br>" . $conn->error;
}
break;
case 'changeNickname':
// 닉네임 변경 로직
$id = $postData['id'];
$newNickname = $postData['newNickname'];
$sql = "UPDATE users SET nickname='$newNickname' WHERE id='$id'";
if ($conn->query($sql) === TRUE) {
echo "Record updated successfully";
} else {
echo "Error updating record: " . $conn->error;
}
break;
default:
echo "Invalid action";
}
} else {
echo "Action not set";
}
마찬가지로 Node에서 회원가입과 닉네임 변경을 담당하는 js 파일을 각각 만들 수 도 있다.
SOLID 패턴 중 단일 책임 원칙을 따르자면 하나의 파일은 하나의 기능만 해야 하기에 PHP의 예시처럼 모두 나누어 놓아야 하나, 프로젝트 특성, 확장성, 코드 재사용성 등 여러 가지 상황을 고려해 결정해야 할 부분인 것 같다.
2. 모듈 사용
Node의 경우 다른 js파일을 사용할 때 module.exports + require 를 이용하여 명시적으로 모듈을 내보내고 가져오게 된다.
// math.js
const add = (a, b) => {
return a + b;
}
const subtract = (a, b) => {
return a - b;
}
module.exports = {
add,
subtract
}
// main.js
const math = require('./math');
console.log(math.add(5, 3)); // 8
console.log(math.subtract(5, 3)); // 2
PHP는 내보내는 과정이 없어도 가져오는데 문제가 없다.
// math.php
<?php
function add($a, $b) {
return $a + $b;
}
function subtract($a, $b) {
return $a - $b;
}
?>
// main.php
<?php
require 'math.php';
echo add(5, 3); // 8
echo subtract(5, 3); // 2
?>
Node (export - require)
- 명시적으로 모듈을 내보내고 가져오기 때문에 모듈 간의 의존성을 파악하기 쉽다.
- 동일한 모듈을 여러 번 가져와도 한 번만 캐싱되어 성능상 장점이 있다.
PHP
- 내보내는 과정이 없기 때문에 모듈 간의 의존성을 파악하기 어렵다.
- Node의 모듈 시스템과 달리 파일을 매번 읽어와야 하는 단점이 있다.
3. 비동기 vs 동기
// Node
const fs = require('fs').promises;
async function readFileAsync() {
try {
const data = await fs.readFile('example.txt', 'utf8');
console.log(data);
} catch (err) {
console.error(err);
}
}
readFileAsync();
console.log('파일 읽기 요청을 기다리는 중...');
// PHP
<?php
// 파일을 동기적으로 읽어옵니다.
$data = file_get_contents('example.txt');
echo $data;
?>
Node의 경우 비동기 작업이 가능하기 때문에 파일을 읽어오는 동안 '기다리는 중..." 과 같은 작업을 수행할 수 있다.
반면 PHP는 동기적으로 동작하기 때문에 파일을 읽는 동안 다른 작업을 수행할 수 없다. 따라서 파일 읽기가 완료된 후에 해당 데이터가 출력된다.
사용자에게 직접 보여지고 UX에 신경을 많이 써야 하는 프론트 엔드 개발에선 비동기 방식을 중요하게 생각하기 때문에 같은 언어를 사용하는 Node에 영향을 준 것이 아닌가 생각한다.
https://radixweb.com/blog/node-js-vs-php
https://www.excellentwebworld.com/node-js-vs-php/