⭐️ ReactExample7 – server + swift
👉🏻 아래는 GET,POST,PATCH,PUT을 Swift에서 구현한 예제입니다.
Below is an example of implementing GET, POST, PATCH, and PUT in Swift.
👉🏻 리액트로 구현한 예제는 아래에 링크된 포스트를 참조하세요
For an example implemented with React, please refer to the post linked below.
👉🏻 Nodejs Express Server(Backend)
✔️Server.js
const express = require("express");
const app = express();
const PORT = 3000;
app.use(express.json());
// 가상의 데이터베이스 / Virtual Database
let users = [{ id: "1", name: "V", age: 20, nickname: "Cyberpunk2077" }];
// GET: 특정 유저 조회 / Specific user inquiry
app.get("/users/:id", (req, res) => {
const user = users.find((u) => u.id === req.params.id);
user
? res.json(user)
: res.status(404).json({ message: "유저 없음/No user" });
});
// POST: 새로운 유저 생성 / Create new user
app.post("/users", (req, res) => {
const newUser = { id: String(users.length + 1), ...req.body };
users.push(newUser);
res.status(201).json(newUser);
});
// PATCH: 데이터의 '일부'만 수정 (기존 데이터 유지)
// Modify only 'part' of the data (keep existing data)
app.patch("/users/:id", (req, res) => {
const index = users.findIndex((u) => u.id === req.params.id);
if (index !== -1) {
// 기존 값 + 바뀐 값 합치기 / Merge existing value + changed value
users[index] = { ...users[index], ...req.body };
res.json(users[index]);
} else {
res.status(404).json({ message: "유저 없음/No user" });
}
});
// PUT: 데이터 '전체'를 교체 (보내지 않은 필드는 사라짐)
// Replace 'all' data (unsent fields disappear)
app.put("/users/:id", (req, res) => {
const index = users.findIndex((u) => u.id === req.params.id);
if (index !== -1) {
// 기존 내용 무시하고 덮어쓰기 / Ignore existing content and overwrite
users[index] = { id: req.params.id, ...req.body };
res.json(users[index]);
} else {
res.status(404).json({ message: "유저 없음/No user" });
}
});
// DELETE: 데이터 삭제 / data deletion
app.delete("/users/:id", (req, res) => {
users = users.filter((u) => u.id !== req.params.id);
res.json({ message: `User ${req.params.id} 삭제 완료 / Deletion complete` });
});
app.listen(PORT, () => console.log(`Server: http://localhost:${PORT}`));
👉🏻 Swift(Frontend)
✔️ ContentView
import SwiftUI
// 데이터 모델 정의 / Data model definition
struct User: Codable, Identifiable {
let id: Int?
var name: String?
var age: Int?
var nickname: String?
}
struct MessageResponse: Codable {
let message: String
}
struct ContentView: View {
@State private var userId: String = "1"
@State private var userData: User? = nil
@State private var log: String = ""
// 서버 기본 URL / Server Base URL
let baseURL = "http://localhost:3000/users"
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 20) {
Text("HTTP Methods Playground")
.font(.title).bold()
TextField("User ID", text: $userId)
.textFieldStyle(RoundedBorderTextFieldStyle())
.keyboardType(.numberPad)
// 버튼 그룹 / Button Group
VStack(spacing: 10) {
HStack {
methodButton("GET (조회/Search)", color: Color(.systemGray5)) { getUser() }
methodButton("POST (생성/Creation)", color: Color(red: 0.8, green: 1, blue: 0.8)) { createUser() }
}
HStack {
methodButton("PATCH (일부수정/modifications)", color: Color(red: 1, green: 0.95, blue: 0.8)) { patchUser() }
methodButton("PUT (전체교체/replacement)", color: Color(red: 1, green: 0.8, blue: 0.8)) { putUser() }
}
methodButton("DELETE (삭제/Deletion)", color: Color(.systemGray6)) { deleteUser() }
}
HStack(alignment: .top, spacing: 20) {
// 현재 데이터 상세 / Current Data State
VStack(alignment: .leading) {
Text("현재 데이터 상세 / Current data details").font(.headline)
if let user = userData {
VStack(alignment: .leading) {
Text("ID: \(user.id ?? 0)")
Text("이름/Name: \(user.name ?? "없음/none")")
Text("나이/Age: \(user.age != nil ? "\(user.age!)" : "없음/none")")
Text("닉네임/Nickname: \(user.nickname ?? "없음/none")")
}
.padding()
.border(Color.blue)
} else {
Text("조회된 데이터가 없습니다./No data was retrieved.")
}
}
.frame(maxWidth: .infinity, alignment: .leading)
// 서버 원본 응답 / Server original response(Raw JSON)
VStack(alignment: .leading) {
Text("서버 응답 / server response").font(.headline)
Text(log)
.font(.system(.caption, design: .monospaced))
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(.systemGray6))
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
.padding()
}
}
// MARK: - HTTP Methods
func getUser() {
performRequest(url: "\(baseURL)/\(userId)", method: "GET")
}
func createUser() {
let body: [String: Any] = ["name": "NewUser", "age": 30, "nickname": "SilverHand"]
performRequest(url: baseURL, method: "POST", body: body)
}
func patchUser() {
let body: [String: Any] = ["nickname": "Johnny"]
performRequest(url: "\(baseURL)/\(userId)", method: "PATCH", body: body)
}
func putUser() {
// PUT은 보통 리소스 전체를 갈아끼우므로 nickname을 생략하면 null이 될 가능성을 시뮬레이션합니다.
// Since PUT usually replaces the entire resource, omitting nickname simulates the possibility that it will be null.
let body: [String: Any] = ["name": "John", "age": 25]
performRequest(url: "\(baseURL)/\(userId)", method: "PUT", body: body)
}
func deleteUser() {
Task {
// Task를 통해 메인 액터와 비동기 컨텍스트를 연결
// Connect the main actor and the asynchronous context through Task
guard let url = URL(string: "\(baseURL)/\(userId)") else { return }
var request = URLRequest(url: url)
request.httpMethod = "DELETE"
do {
let (data, _) = try await URLSession.shared.data(for: request)
let decoded = try JSONDecoder().decode(MessageResponse.self, from: data)
// UI 업데이트는 메인 스레드에서
// UI updates are done on the main thread
await MainActor.run {
self.userData = nil
self.log = decoded.message
}
} catch {
print("Error: \(error)")
}
}
}
// MARK: - Helper Methods
// 서버에 요청 / Request to server
func performRequest(url urlString: String, method: String, body: [String: Any]? = nil) {
guard let url = URL(string: urlString) else { return }
var request = URLRequest(url: url)
request.httpMethod = method
if let body = body {
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
}
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return }
// UI 업데이트는 메인 스레드에서
// UI updates are done on the main thread
DispatchQueue.main.async {
self.log = String(data: data, encoding: .utf8) ?? ""
if let decodedUser = try? JSONDecoder().decode(User.self, from: data) {
self.userData = decodedUser
} else {
self.userData = nil
}
}
}.resume()
}
func methodButton(_ title: String, color: Color, action: @escaping () -> Void) -> some View {
Button(action: action) {
Text(title)
.font(.caption)
.padding(10)
.frame(maxWidth: .infinity)
.background(color)
.foregroundColor(.black)
.cornerRadius(5)
}
}
}
#Preview {
ContentView()
}
👉🏻 스크린샷/ScreenShot
