- java와 javascript의 스타일 차이. 태생이다르다
- node js의 등장
- epoll과 같은 os method의 발전
- java의 발전
프로그래밍 스타일 비교
Java Style: Sequential and Conditional
In Java, you call a function, wait for the result, and then use if statements to control logic:
@RestController
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/user")
public String getUserInfo() {
return userService.getUserData();
}
}
int status = service.getStatus();
if (status == 200) {
System.out.println("Success!");
} else {
System.out.println("Failed!");
}
<button id="loadUserBtn">Load User</button>
<div id="userInfo"></div>
<script>
document.getElementById("loadUserBtn").addEventListener("click", async () => {
try {
const res = await fetch("http://localhost:8080/user"); // calls Java controller
const text = await res.text();
document.getElementById("userInfo").textContent = text;
} catch (e) {
console.error("Failed to load user:", e);
}
});
</script>
- Straightforward
- Synchronous
- Top-down logic
- You are in control of the execution order
JavaScript Style (especially async): Assign logic to callback
“When the result eventually comes, then run this function.”
- Non-blocking
- You assign logic to the result via a callback (then)
- The engine calls your function later
- You give up control in exchange for asynchronous flow
fetch('/status')
.then(response => {
if (response.status === 200) {
console.log("Success!");
} else {
console.log("Failed!");
}
});
Java: Multi-threaded, blocking I/O
- Every request can get its own thread.
- You call a function → the thread blocks (waits) until result is returned.
- You scale by adding more threads (limited by memory and CPU).
String result = restTemplate.getForObject(url); // blocks thread
JavaScript: Single-threaded, non-blocking with event loop
- JS uses one thread.
- Instead of blocking, it says:
- “When the result is ready, call this function.”
- The event loop constantly checks:
- “Is anything ready to run now?”
fetch(url).then(res => res.json()).then(data => console.log(data)); // non-blocking
Type of Difference | Java | JavaScript |
Execution Model | Thread-per-task (blocking) | Event loop (non-blocking) |
Concurrency model | Multi-threading | Single-threaded + event loop |
I/O handling | Blocking I/O (unless using async APIs like CompletableFuture, NIO) | Non-blocking I/O by default |
Built for | Servers, business logic | UI interaction, asynchronous web apps |
Paradigm | Imperative by default | Event-driven by default |
Yes — even in Spring MVC, there is something listening for HTTP requests.
Your @Controller is a response to an event — just like a JavaScript event listener.
Spring and JavaScript aren’t so different after all — it’s just that Spring hides the event-driven parts, while JavaScript exposes them.
1. JavaScript was born for the browser
- JS was designed in 1995 for interactivity on web pages
- In the browser, you can't block the UI — otherwise the page freezes
- So it had to be asynchronous by design
button.addEventListener("click", () => {
// react to user input
});
2. Then came Node.js (2009)
Node.js said: "What if we use the event loop to build web servers?"
Instead of using one thread per request like Java/Spring/Tomcat, Node.js:
- Used a single-threaded event loop
- Delegated I/O to the OS using non-blocking APIs
- Handled thousands of concurrent requests using few threads
- java는 값을 값을 받을때까지 기다려서 한다면
- javascript는 callback을 사용해서 event를 알아서 처리하도록 한다
-
3. Why it became popular
✅ Scales well with I/O-bound workloads
Imagine:
- 10,000 users calling an API
- Most of the time is spent waiting for DB or external APIs
🟦 Blocking model (Java/Tomcat):
10,000 users → 10,000 threads → big memory, CPU usage
🟨 Non-blocking model (Node.js/WebFlux):
10,000 users → 20 threads (just enough to listen + react) → super efficient
Node.js Structure
Node.js is a JavaScript runtime built on V8 (Google’s JavaScript engine from Chrome)
But to run JavaScript outside the browser, it needed to:
- Handle files
- Handle sockets
- Handle timers
- Handle async I/O like a real OS-level application

is it an OS thread?
The thread pool that handles I/O in Node.js (via libuv) is made up of native OS threads
In Java Spring: Threads are application-level and managed by you or Spring
You (or Spring) are in charge of thread creation, usage, and termination.
What really happens when a Java thread does I/O (like reading from a socket or file):
- 🧵 A Java thread (which is a real OS thread) calls a blocking method
→ e.g., InputStream.read() or JDBC connection.executeQuery() - 💤 The thread enters a "waiting" (blocked) state in the OS
- It’s not running or consuming CPU
- It is sleeping, waiting for I/O to complete
- ⚙️ The OS is monitoring the I/O resource (disk, network, etc.)
- 🛎 When the data is ready (e.g., response from DB, file loaded), the OS interrupts the thread
- This is usually done via a hardware interrupt, polling, or epoll/select/kqueue depending on platform
- 🔁 The OS tells the JVM scheduler: “This thread can run again”
- 🏃 The thread wakes up and continues executing the rest of your code
No, Node.js with libuv does NOT need 1000 threads for 1000 I/O tasks — unless all 1000 are blocking tasks that must be offloaded (like file or DNS I/O).
✅ No threads are assigned to monitor sockets
✅ The event loop + OS pollers handle that efficiently
✅ libuv threads are used only when blocking cannot be avoided
Only when a truly blocking task is needed (e.g. file read, DNS), libuv sends it to a libuv thread pool thread
Node.js is efficient because most I/O tasks (especially sockets/network) are handled non-blocking by the OS itself — using epoll, kqueue, or IOCP — without blocking any thread.
1. Non-blocking I/O (like sockets, HTTP, TCP, etc.)
These are handled via OS-level async APIs — no libuv thread is used
- libuv registers the file descriptor with epoll (Linux), kqueue (macOS), or IOCP (Windows)
- The event loop waits for the OS to notify when the socket is ready
- No thread blocks
- 1000 concurrent sockets = ✅ 1 event loop + OS monitoring
✅ Extremely scalable
2. Blocking I/O (like fs.readFile, crypto.pbkdf2, DNS)
These are delegated to libuv’s thread pool, so each thread does block
- libuv’s thread pool has limited size (default: 4, max: 128 via UV_THREADPOOL_SIZE)
- If you dispatch 1000 disk reads at once:
- ✅ 4 (or 8, or 16...) threads start working
- 🕒 The rest are queued in a job queue
- The event loop stays free, still serving HTTP/socket traffic
❗ So yes — for CPU- or disk-heavy workloads, Node.js is not magic
- It trades latency for non-blocking UI/network performance
'개발기술 > 설계|디자인패턴' 카테고리의 다른 글
Code Structuring Technique (0) | 2025.03.17 |
---|---|
비동기 프로그래밍 패턴과 처리 방식 (1) | 2024.11.10 |
클래스 다이어그램 (1) | 2024.09.10 |
프로젝트 설계 및 문서화 (0) | 2024.08.14 |
디자인패턴 (0) | 2024.08.10 |