๐_๊ถ๊ทน์_์น_ํ๋ ์์ํฌ_์๋_๋๊ฒฐ[20251230084336]
Source: Dev.to
๋ฒ์ญํ ํ ์คํธ๊ฐ ์ ๊ณต๋์ง ์์์ต๋๋ค. ๋ฒ์ญ์ด ํ์ํ ๋ณธ๋ฌธ์ ์๋ ค์ฃผ์๋ฉด ํ๊ตญ์ด๋ก ๋ฒ์ญํด ๋๋ฆฌ๊ฒ ์ต๋๋ค.
๐ก ํ ์คํธ ๋ฐฐ๊ฒฝ
2024๋ ์๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ ๊ธฐ๋์น๊ฐ ๋ฐ๋ฆฌ์ด ์์ค์ ์๋ต ์๊ฐ์ ๋๋ฌํ์ต๋๋ค. ๋๋ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ์น ํ๋ ์์ํฌ๋ค์ ํ ๋ฌ ๋์ ๋ฒค์น๋งํนํ์ต๋๋ค:
| ํ๋ ์์ํฌ | ์นดํ ๊ณ ๋ฆฌ |
|---|---|
| Tokio | Rust ๋น๋๊ธฐ ๋ฐํ์ |
| Hyperlane | Rust ๊ณ ์ฑ๋ฅ ํ๋ ์์ํฌ |
| Rocket | Rust ์น ํ๋ ์์ํฌ |
| Rust Standard Library | ์ ์์ค Rust |
| Gin | Go ์น ํ๋ ์์ํฌ |
| Go Standard Library | Go net/http |
| Node Standard Library | Node.js http |
ํ ์คํธ ํ๊ฒฝ
- ์๋ฒ: Intel XeonโฏE5โ2686โฏv4 @โฏ2.30โฏGHz
- ๋ฉ๋ชจ๋ฆฌ: 32โฏGB DDR4
- ๋คํธ์ํฌ: Gigabit Ethernet
- OS: Ubuntuโฏ20.04โฏLTS
๐ ์ ์ฒด ์ฑ๋ฅ ๋น๊ต ๋ฐ์ดํฐ
๐ KeepโAlive ํ์ฑํ
wrk ์คํธ๋ ์ค ํ ์คํธ
๋์ ์ฐ๊ฒฐ 360๊ฐ, ์ง์ ์๊ฐ 60โฏ์ด
| ํ๋ ์์ํฌ | QPS | ์ง์ฐ ์๊ฐ | ์ ์ก ์๋ | ์์ |
|---|---|---|---|---|
| Tokio | 340,130.92 | 1.22โฏms | 30.17โฏMB/s | ๐ฅ |
| Hyperlane | 334,888.27 | 3.10โฏms | 33.21โฏMB/s | ๐ฅ |
| Rocket | 298,945.31 | 1.42โฏms | 68.14โฏMB/s | ๐ฅ |
| Rust StdโLib | 291,218.96 | 1.64โฏms | 25.83โฏMB/s | 4๏ธโฃ |
| Gin | 242,570.16 | 1.67โฏms | 33.54โฏMB/s | 5๏ธโฃ |
| Go StdโLib | 234,178.93 | 1.58โฏms | 32.38โฏMB/s | 6๏ธโฃ |
| Node StdโLib | 139,412.13 | 2.58โฏms | 19.81โฏMB/s | 7๏ธโฃ |
ab ์คํธ๋ ์ค ํ ์คํธ
๋์ ์ฐ๊ฒฐ 1000๊ฐ, 1โฏM ์์ฒญ
| ํ๋ ์์ํฌ | QPS | ์ง์ฐ ์๊ฐ | ์ ์ก ์๋ | ์์ |
|---|---|---|---|---|
| Hyperlane | 316,211.63 | 3.162โฏms | 32,115.24โฏKB/s | ๐ฅ |
| Tokio | 308,596.26 | 3.240โฏms | 28,026.81โฏKB/s | ๐ฅ |
| Rocket | 267,931.52 | 3.732โฏms | 70,907.66โฏKB/s | ๐ฅ |
| Rust StdโLib | 260,514.56 | 3.839โฏms | 23,660.01โฏKB/s | 4๏ธโฃ |
| Go StdโLib | 226,550.34 | 4.414โฏms | 34,071.05โฏKB/s | 5๏ธโฃ |
| Gin | 224,296.16 | 4.458โฏms | 31,760.69โฏKB/s | 6๏ธโฃ |
| Node StdโLib | 85,357.18 | 11.715โฏms | 4,961.70โฏKB/s | 7๏ธโฃ |
๐ KeepโAlive ๋นํ์ฑํ
wrk ์คํธ๋ ์ค ํ ์คํธ
๋์ ์ฐ๊ฒฐ 360๊ฐ, ์ง์ ์๊ฐ 60โฏ์ด
| ํ๋ ์์ํฌ | QPS | ์ง์ฐ ์๊ฐ | ์ ์ก ์๋ | ์์ |
|---|---|---|---|---|
| Hyperlane | 51,031.27 | 3.51โฏms | 4.96โฏMB/s | ๐ฅ |
| Tokio | 49,555.87 | 3.64โฏms | 4.16โฏMB/s | ๐ฅ |
| Rocket | 49,345.76 | 3.70โฏms | 12.14โฏMB/s | ๐ฅ |
| Gin | 40,149.75 | 4.69โฏms | 5.36โฏMB/s | 4๏ธโฃ |
| Go StdโLib | 38,364.06 | 4.96โฏms | 5.12โฏMB/s | 5๏ธโฃ |
| Rust StdโLib | 30,142.55 | 13.39โฏms | 2.53โฏMB/s | 6๏ธโฃ |
| Node StdโLib | 28,286.96 | 4.76โฏms | 3.88โฏMB/s | 7๏ธโฃ |
ab ์คํธ๋ ์ค ํ ์คํธ
๋์ ์ฐ๊ฒฐ 1000๊ฐ, 1โฏM ์์ฒญ
| ํ๋ ์์ํฌ | QPS | ์ง์ฐ ์๊ฐ | ์ ์ก ์๋ | ์์ |
|---|---|---|---|---|
| Tokio | 51,825.13 | 19.296โฏms | 4,453.72โฏKB/s | ๐ฅ |
| Hyperlane | 51,554.47 | 19.397โฏms | 5,387.04โฏKB/s | ๐ฅ |
| Rocket | 49,621.02 | 20.153โฏms | 11,969.13โฏKB/s | ๐ฅ |
| Go StdโLib | 47,915.20 | 20.870โฏms | 6,972.04โฏKB/s | 4๏ธโฃ |
| Gin | 47,081.05 | 21.240โฏms | 6,436.86โฏKB/s | 5๏ธโฃ |
| Node StdโLib | 44,763.11 | 22.340โฏms | 4,983.39โฏKB/s | 6๏ธโฃ |
| Rust StdโLib | 31,511.00 | 31.735โฏms | 2,707.98โฏKB/s | 7๏ธโฃ |
๐ฏ ๊น์ด ์๋ ์ฑ๋ฅ ๋ถ์
๐ KeepโAlive ํ์ฑํ
- Tokio๊ฐ 340โฏk QPS๋ก ์ ๋๋ฅผ ๋ฌ๋ฆฌ์ง๋ง, Hyperlane์ 1.5โฏ% ๋ค์ฒ์ ธ (334โฏk QPS)์ ๋๋ค.
- Hyperlane์ 33.21โฏMB/s ์ ์ก ์๋๊ฐ Tokio์ 30.17โฏMB/s๋ฅผ ๋ฅ๊ฐํ์ฌ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ํจ์จ์ด ๋ฐ์ด๋จ์ ๋ณด์ฌ์ค๋๋ค.
abํ ์คํธ์์ Hyperlane์ Tokio๋ณด๋ค ์ฐ์ํ ์ฑ๋ฅ์ ๋ณด์ด๋ฉฐ (316โฏk vs. 308โฏk QPS), ์ฅ๊ธฐ ์ฐ๊ฒฐ์ ๋ํ ์ง์ ํ ์ฑ๋ฅ ์ฑํผ์ธ์ด ๋ฉ๋๋ค.
๐ KeepโAlive ๋นํ์ฑํ
- ์งง์ ์๋ช
์ ์ฐ๊ฒฐ์์๋ Hyperlane์ด ๋ค์
wrkํ ์คํธ์์ ์ต๊ณ (51โฏk QPS)๋ฅผ ๊ธฐ๋กํ๊ณ , Tokio์ 4โฏ% ์ด๋ด ์ฐจ์ด๋ก ์ ์ง๋ฉ๋๋ค. abํ ์คํธ์์๋ Tokio๊ฐ ๋ค์ ์ ๋๋ฅผ ์ฐจ์งํ์ง๋ง, ์ฐจ์ด (โโฏ0.5โฏ%)๊ฐ ์ผ๋ฐ์ ์ธ ์ธก์ ์ก์ ๋ฒ์์ ํด๋นํด ๋ ํ๋ ์์ํฌ ๋ชจ๋ ์ฐ๊ฒฐ ๋ณ๋์ ๋งค์ฐ ์ ์ฒ๋ฆฌํจ์ ๋ํ๋ ๋๋ค.
๐ป ์ฝ๋ ๊ตฌํ ๋น๊ต
๐ข Node.js ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello');
});
server.listen(60000, '127.0.0.1');
๊ฐ๋จํ์ง๋ง, ๋จ์ผ ์ค๋ ๋ ์ด๋ฒคํธ ๋ฃจํ๋ ๋๊ท๋ชจ ๋์์ฑ ํ์์ ๋น ๋ฅด๊ฒ ๋ณ๋ชฉ์ด ๋ฉ๋๋ค. ํ ์คํธ์์๋ Node.js ์๋ฒ๊ฐ 811,908๊ฐ์ ์คํจ ์์ฒญ์ ๊ธฐ๋กํ์ต๋๋ค.
๐น Go ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":60000", nil)
}
Go์ ๊ณ ๋ฃจํด ๋ชจ๋ธ์ ํจ์ฌ ๋์ ๋์์ฑ์ ์ ๊ณตํ์ฌ 234โฏk QPS๋ฅผ ๋ฌ์ฑํ์ต๋๋คโNode๋ณด๋ค ํฌ๊ฒ ๋์ง๋ง ์ต์์ Rust ํ๋ ์์ํฌ๋ณด๋ค๋ ์์ง ๋ค์ฒ์ง๋๋ค.
๐ Rust ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ (hyper)
use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};
async fn hello(_req: Request) -> Result<Response<Body>, hyper::Error> {
Ok(Response::new(Body::from("Hello")))
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let make_svc = make_service_fn(|_conn| async {
Ok::<_, hyper::Error>(service_fn(hello))
});
let addr = ([127, 0, 0, 1], 60000).into();
let server = Server::bind(&addr).serve(make_svc);
println!("Listening on http://{}", addr);
server.await?;
Ok(())
}
Tokio์ ํจ๊ป hyper๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฒฌ๊ณ ํ ๊ธฐ๋ณธ ์ฑ๋ฅ(โโฏ291โฏk QPS)์ ์ ๊ณตํฉ๋๋ค. ์์ ๋ ๋ฒจ ํ๋ ์์ํฌ(Tokio, Hyperlane, Rocket)๋ ์ด ๊ธฐ๋ฐ ์์ ๊ตฌ์ถ๋์ด ์ฑ๋ฅ์ ๋์ฑ ํฅ์์ํค๋ ์ต์ ํ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
๐ Takeaways
- Hyperlane์ ์ ์ก ์๋ ์งํ์์ ์ผ๊ด๋๊ฒ Tokio๋ฅผ ๋ฅ๊ฐํ๊ฑฐ๋ ๋์ ํฉ๋๋ค.
- KeepโAlive๋ ์์์ ํฐ ์ํฅ์ ๋ฏธ์นฉ๋๋ค; ์ฐ๊ฒฐ ์ฌ์ฌ์ฉ์ ๋ฐ์ด๋ ํ๋ ์์ํฌ(Tokio, Hyperlane)๋ ์ด๋ฅผ ํ์ฑํํ์ ๋ ์ฐ์๋ฅผ ์ ํฉ๋๋ค.
- Node.js๋ ์์ ์ฒ๋ฆฌ๋ ๋ฉด์์ ์ฌ์ ํ ๋ค์ฒ์ง๊ณ , Go๋ ์ค๊ฐ ์ ๋์ด๋ฉฐ, Rust ๊ธฐ๋ฐ ์๋ฃจ์ ์ด ๊ณ ์ฑ๋ฅ ๊ณ์ธต์ ์ฅ์ ํฉ๋๋ค.
- ํ๋ ์์ํฌ๋ฅผ ์ ํํ ๋๋ ์์ QPS๋ฟ๋ง ์๋๋ผ ์ ์ก ์๋, ์ง์ฐ ์๊ฐ, ๊ทธ๋ฆฌ๊ณ ์ํฌ๋ก๋์ ๋ง๋ ์ฐ๊ฒฐ ๊ด๋ฆฌ ํน์ฑ๋ ํจ๊ป ๊ณ ๋ คํ์ธ์.
์์ ๋ฒค์น๋งํฌ ๋ก๊ทธ๊ฐ ํ์ํ๊ฑฐ๋ Hyperlane ๋ด๋ถ์ ๋ํด ๋ ๊น์ด ์์๋ณด๊ณ ์ถ๋ค๋ฉด ์ธ์ ๋ ์ง ์ฐ๋ฝ ์ฃผ์ธ์.
Rust ๊ตฌํ
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
fn handle_client(mut stream: TcpStream) {
let response = "HTTP/1.1 200 OK\r\n\r\nHello";
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
fn main() {
let listener = TcpListener::bind("127.0.0.1:60000").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_client(stream);
}
}
์ ์ฒด ํ๋ฉด ๋ชจ๋ ์ง์
์ ์ฒด ํ๋ฉด ๋ชจ๋ ์ข
๋ฃ
Rust์ ์์ ๊ถ ์์คํ ๊ณผ ์ ๋กโ์ฝ์คํธ ์ถ์ํ๋ ์ค์ ๋ก ๋ฐ์ด๋ ์ฑ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ํ ์คํธ ๊ฒฐ๊ณผ, Rust ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ 291,218.96 QPS๋ฅผ ๋ฌ์ฑํ์ผ๋ฉฐ, ์ด๋ ์ด๋ฏธ ๋งค์ฐ ์ธ์์ ์ ๋๋ค. ํ์ง๋ง ๊ณ ๋์์ฑ ์๋๋ฆฌ์ค์์๋ Rust์ ์ฐ๊ฒฐ ๊ด๋ฆฌ์ ์์ง ์ต์ ํ ์ฌ์ง๊ฐ ์์์ ๋ฐ๊ฒฌํ์ต๋๋ค.
๐ฏ ์ฑ๋ฅ ์ต์ ํ ์ ๋ต ๋ถ์
๐ง ์ฐ๊ฒฐ ๊ด๋ฆฌ ์ต์ ํ
๋น๊ต ํ ์คํธ๋ฅผ ํตํด ํต์ฌ ์ฑ๋ฅ ์ต์ ํ ํฌ์ธํธ์ธ ์ฐ๊ฒฐ ๊ด๋ฆฌ๋ฅผ ๋ฐ๊ฒฌํ์ต๋๋ค. Hyperlane ํ๋ ์์ํฌ๋ ์ฐ๊ฒฐ ์ฌ์ฌ์ฉ์ ๋ฐ์ด๋๋ฉฐ, ์ด๋ KeepโAlive ํ ์คํธ์์ ๋ฐ์ด๋ ์ฑ๋ฅ์ ๋ณด์ด๋ ์ด์ ๋ฅผ ์ค๋ช ํฉ๋๋ค.
์ ํต์ ์ธ ์น ํ๋ ์์ํฌ๋ ์ฐ๊ฒฐ์ ์ฒ๋ฆฌํ ๋ ์ข ์ข ๅคง้ไธดๆถๅฏน่ฑก(๋ง์ ์์ ๊ฐ์ฒด)๋ฅผ ์์ฑํ์ฌ GC ์๋ ฅ์ ์ฆ๊ฐ์ํต๋๋ค. Hyperlane์ ๊ฐ์ฒด ํ ๊ธฐ์ ์ ์ฑํํด ๋ฉ๋ชจ๋ฆฌ ํ ๋น ์ค๋ฒํค๋๋ฅผ ํฌ๊ฒ ์ค์ ๋๋ค.
๐ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ์ต์ ํ
๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๋ ์น ํ๋ ์์ํฌ ์ฑ๋ฅ์ ๋ ๋ค๋ฅธ ํต์ฌ ์์์ ๋๋ค. ํ ์คํธ์์ Rust์ ์์ ๊ถ ์์คํ ์ด ์ค์ ๋ก ๋ฐ์ด๋ ์ฑ๋ฅ์ ์ ๊ณตํ์ง๋ง, ์ค์ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ๊ฐ๋ฐ์๊ฐ ๋ณต์กํ ๋ผ์ดํํ์ ๋ฌธ์ ๋ฅผ ๋ค๋ฃจ์ด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค.
Hyperlane์ Rust์ ์์ ๊ถ ๋ชจ๋ธ๊ณผ ๋ง์ถคํ ๋ฉ๋ชจ๋ฆฌ ํ์ ๊ฒฐํฉํด zeroโcopy ๋ฐ์ดํฐ ์ ์ก์ ๊ตฌํํฉ๋๋ค. ์ด๋ ๋์ฉ๋ ํ์ผ ์ ์ก์ ํนํ ํจ๊ณผ์ ์ ๋๋ค.
โก ๋น๋๊ธฐ ์ฒ๋ฆฌ ์ต์ ํ
๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ํ๋ ์น ํ๋ ์์ํฌ์ ํต์ฌ ๊ธฐ๋ฅ์ ๋๋ค. Tokio๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ์์ ์ข์ ์ฑ๋ฅ์ ๋ณด์ด์ง๋ง, ๋์ ๋์์ฑ ์ํฉ์์ ์์ ์ค์ผ์ค๋ง ์๊ณ ๋ฆฌ์ฆ์ด ๋ณ๋ชฉ ํ์์ ๊ฒช์ต๋๋ค.
Hyperlane์ ์์คํ ๋ถํ์ ๋ฐ๋ผ ์์ ํ ๋น์ ๋์ ์ผ๋ก ์กฐ์ ํ๋ ๋ณด๋ค ์ง๋ณด๋ ์์ ์ค์ผ์ค๋ง ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํด ํธ๋ํฝ ๊ธ์ฆ ์ํฉ์์ ํนํ ํจ๊ณผ์ ์ ๋๋ค.
๐ฏ Practical Application Recommendations
๐ช Eโcommerce Website Scenarios
Performance is money for eโcommerce sites. In my tests, Hyperlane excels in product listings, user authentication, and order processing.
- Recommendation: Use Hyperlane for core business systems, especially CPUโintensive tasks like product search and recommendation algorithms.
- Static resources: Consider dedicated web servers such as Nginx.
๐ฌ Social Platform Scenarios
Social platforms involve numerous connections and frequent messages. Hyperlane shines in WebSocket connection management, handling hundreds of thousands of concurrent connections.
- Recommendation: Build messageโpush systems with Hyperlane, combined with an inโmemory database like Redis for realโtime delivery.
- Complex business logic (e.g., user relationships): Consider GraphQL or similar technologies.
๐ข Enterprise Application Scenarios
Enterprise apps need to handle complex processes and data consistency. Hyperlane provides strong support for transaction processing, ensuring data integrity.
- Recommendation: Use Hyperlane for core business systems, paired with relational databases like PostgreSQL for persistence.
- CPUโintensive tasks (e.g., report generation): Leverage asynchronous processing.
๐ฎ ํฅํ ๊ฐ๋ฐ ํธ๋ ๋
๐ ๊ทนํ ์ฑ๋ฅ
ํ๋์จ์ด๊ฐ ๊ฐ์ ๋จ์ ๋ฐ๋ผ ํ๋ ์์ํฌ๋ ๋ฐฑ๋ง ์์ค QPS์ ๋ง์ดํฌ๋ก์ด ์์ค ์ง์ฐ์ ๋ชฉํ๋ก ํ ๊ฒ์ ๋๋ค.
๐ง ๊ฐ๋ฐ ๊ฒฝํ ์ต์ ํ
์์ํ ์ฑ๋ฅ์ ๋์ด, ๊ฐ๋ฐ์๋ ๋ ๋์ ํด๋ง, ๋๋ฒ๊น ๋ฐ ๋ชจ๋ํฐ๋ง์ ํตํด ๊ณ ์ฑ๋ฅ ๊ฐ๋ฐ์ ๋ณด๋ค ์ฝ๊ฒ ์ ๊ทผํ ์ ์๊ฒ ๋ฉ๋๋ค.
๐ ํด๋ผ์ฐ๋โ๋ค์ดํฐ๋ธ ์ง์
ํ๋ ์์ํฌ๋ ์ปจํ ์ด๋ํ์ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ์ ๋ํ ์ง์์ ์ฌํํ์ฌ, ๋ด์ฅ๋ ์๋น์ค ๋์ค์ปค๋ฒ๋ฆฌ, ๋ก๋ ๋ฐธ๋ฐ์ฑ, ์ํท ๋ธ๋ ์ดํน ๋ฐ ๊ด๋ จ ๊ธฐ๋ฅ์ ์ ๊ณตํ ๊ฒ์ ๋๋ค.
๐ฏ ์์ฝ
์ด๋ฒ ํ ์คํธ๋ฅผ ํตํด ํ๋ ์น ํ๋ ์์ํฌ์ ์ฑ๋ฅ ์ ์ฌ๋ ฅ์ด ๋ค์ ํ์ธ๋์์ต๋๋ค. Hyperlane์ ๋ฑ์ฅ์ Rust๋ฅผ ํ์ฉํ ์น ๊ฐ๋ฐ์ ๋ฌดํํ ๊ฐ๋ฅ์ฑ์ ๋ณด์ฌ์ค๋๋ค. ์ผ๋ถ ๋ฒค์น๋งํฌ์์๋ Tokio๊ฐ Hyperlane๋ณด๋ค ๋ฐ์ด๋์ง๋ง, Hyperlane์ ์ ๋ฐ์ ์ธ ์ฑ๋ฅ๊ณผ ์์ ์ฑ์์ ๋ ์ฐ์ํ ๊ฒฐ๊ณผ๋ฅผ ์ ๊ณตํฉ๋๋ค.
์๋์ด ๊ฐ๋ฐ์๋ก์ ํ๋ ์์ํฌ ์ ํ ์ ๋จ์ํ ์์ ์ฑ๋ฅ๋ฟ๋ง ์๋๋ผ ๊ฐ๋ฐ ๊ฒฝํ, ์ํ๊ณ, ์ปค๋ฎค๋ํฐ ์ง์๋ ๊ณ ๋ คํด์ผ ํ๋ค๊ณ ์กฐ์ธํฉ๋๋ค. Hyperlane์ ์ด๋ฌํ ์ฌ๋ฌ ์ธก๋ฉด์์ ๋์ ์ ์๋ฅผ ๋ฐ์ผ๋ฉฐ ๅฐ่ฏํ ๊ฐ์น๊ฐ ์์ต๋๋ค.
์น ๊ฐ๋ฐ์ ๋ฏธ๋๋ ์ ์ ๋ ์ฑ๋ฅ๊ณผ ํจ์จ์ฑ์ ์ด์ ์ ๋ง์ถ ๊ฒ์ด๋ฉฐ, ์ ๋ Hyperlane์ด ๊ทธ ํ๊ฒฝ์์ ์ ์ ๋ ํฐ ์ญํ ์ ํ ๊ฒ์ด๋ผ๊ณ ๋ฏฟ์ต๋๋ค.
forward to the next breakthrough in web development technology together!
[GitHub Homepage](https://github.com/hyperlane-dev/hyperlane)