Spark ์์ํ๊ธฐ
์น ํ๋ก๊ทธ๋๋ฐ ์
๋ฌธ์ ์ํด Java ์ ๊ฐ๋จํ ์น ํ๋ ์์ํฌ์ธ Spark ๋ฅผ ์์๋ณด์.
์ฐ์ ์ธํ
๋ฆฌ์ ์ด์์ gradle ํ๋ก์ ํธ๋ฅผ ์์ฑํ๋ค.
๋น๋๊ฐ ์๋ฃ๋๋ฉด build.gradle ์ด๋ผ๋ ํ์ผ์ด ์์ฑ๋๋ค.
Spark ๋ฅผ ์ ์ฉํ๊ธฐ ์ํด build.gradle ์ dependency ๋ฅผ ์๋์ ๊ฐ์ด ์ถ๊ฐํ๋ค.
dependencies {
implementation "com.sparkjava:spark-core:2.9.3" // add to build.gradle (for Java users)}
}๊ณต์ Spark ๋ฌธ์์๋ compile ํค์๋๋ฅผ ์ฌ์ฉํ๋ผ๊ณ ๋ช
์๋์ด ์๋ค.
Gradle 7.0 ๋ฒ์ ๋ถํฐ implementation ํค์๋๋ฅผ ๋์ ์ฌ์ฉํ๊ฒ ๋ ๋ณ๊ฒฝ๋์๋ค.
์ด์ Spark ๋ฅผ ์ฌ์ฉํ ์ค๋น๊ฐ ์๋ฃ ๋์๋ค.
import static spark.Spark.*;
public class HelloWorld {
public static void main(String[] args) {
get("/users", (req, res) -> "Hello World");
}
}main ์ ์์ ๊ฐ์ ์ฝ๋๋ฅผ ์ถ๊ฐํ์ฌ ์คํํด๋ณด์.
์คํ์ด ์ฑ๊ณต๋ ์ดํ ๋ธ๋ผ์ฐ์ ๋ฅผ ํตํด localhost:4567/users ์ ์ ์ํด๋ณด์.
์ฑ๊ณต์ ์ผ๋ก Hello World ๊ฐ ์ถ๋ ฅ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
GET ์ฌ์ฉ๋ฒ
public class HelloWorld {
public static void main(String[] args) {
get("/users", (req, res) -> "Hello World");
}
}์ ์ฝ๋๋ HTTP ๋ฉ์๋์ธ GET ์ Spark ์์ ์ฌ์ฉํ๋ ๋ฒ์ด๋ค.
์์ ์ธ๊ธํ localhost:4567/hello ์ ์๋ฏธ๋ถํฐ ์ดํด๋ณด์.
localhost ์์ 4567๋ฒ ํฌํธ์ /hello ์ ์ ์ํ๋ค๋ ์๋ฏธ๋ค.
Spark ์ ๊ธฐ๋ณธ ํฌํธ ๋ฒํธ๋ 4567 ์ด๊ณ port() ๋ฅผ ํตํด ํฌํธ ๋ฒํธ๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค.
public static void get(final String path, final Route route) {
getInstance().get(path, route);
}์ด๋ฒ์ get() ๋ฉ์๋์ ๋ด๋ถ๋ฅผ ์ดํด๋ณด์.
parameter ๋ก path ์ route ๋ฅผ ๋ฐ๊ณ ์๋ค.
ํธ์ถํ๋ ๋ฉ์๋๋ฅผ ๋ค์ ์ดํด๋ณด๋ฉด path ๋ โ/users" ์์ ์ ์ ์๊ณ , route ๋ ๋๋ค์์ด argument ๋ก ์ฃผ์ด์ง๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์ด๋ฅผ ํตํด get() ๋ฉ์๋๊ฐ URL path ๋ฅผ ์ฒซ๋ฒ์งธ argument ๋ก ๋ฐ๊ณ ํด๋น ์ฃผ์์ ์ํ๋๋ ํ์๋ฅผ ๋๋ค์์ผ๋ก ํํํ๊ณ ์๋ค๋ ๊ฒ์ ์์ธกํ ์ ์๋ค.
public class HelloWorld {
public static void main(String[] args) {
get("/users/:name", (req, res) -> "Hello " + req.params(":name"));
}
}๋ค๋ฅธ get() ์ฌ์ฉ ์์๋ฅผ ๋ณด์.
์ด๋ฒ์ path ๊ฐ์ "/users/:name" ์ ์ ๋ฌํ๋ค.
๋ธ๋ผ์ฐ์ ์์ localhost:4567/users/meatsby ๋ฅผ ํธ์ถํ์ ๋, :name ๊ฐ์ meatsby ๊ฐ ํ ๋น๋๋ค.
req.params() ๋ฉ์๋๋ฅผ ํตํด ์ ๋ฌ๋ฐ์ ๊ฐ์ ์๋ก์ด ๋ฌธ์์ด์ ์ถ๊ฐ์์ผ localhost:4567/users/meatsby ์ฃผ์์์ โHello meatsbyโ ๋ฅผ ์ถ๋ ฅํ๊ฒ ๋๋ค.
public class HelloWorld {
public static void main(String[] args) {
get("/users", (req, res) -> "Hello " + req.queryParams("name") + " ๋์ด๋ " + req.queryParams("age"));
}
}๋ ๋ค๋ฅธ get() ์ฌ์ฉ ์์๋ฅผ ๋ณด์.
๋ธ๋ผ์ฐ์ ์์ localhost:4567/users?name=meatsby&age=26 ๋ฅผ ํธ์ถํ๋ค.
?name=meatsby&age=26 ์ด ๊ตฌ๋ฌธ์ ์ฟผ๋ฆฌ์คํธ๋ง์ด๋ผ๊ณ ๋ถ๋ฆฌ๋๋ฐ, name ๊ฐ์ meatsby ๋ฅผ age ๊ฐ์ 26 ์ ์ ๋ฌํ๊ฒ ๋ค๋ ์๋ฏธ๋ฅผ ๊ฐ๊ณ ์๋ค.
์ฟผ๋ฆฌ์คํธ๋ง์ผ๋ก ์ ๋ฌ๋ ๊ฐ๋ค์ req.queryParams() ๋ฉ์๋๋ฅผ ํตํด name ๊ฐ๊ณผ age ๊ฐ์ ๋ฐ์ ์ ๋ฌ๋ฐ์ ๊ฐ์ ์๋ก์ด ๋ฌธ์์ด์ ์ถ๊ฐ์์ผ localhost:4567/users?name=meatsby&age=26 ์ฃผ์์์ โHello meatsby ๋์ด๋ 26โ ์ ์ถ๋ ฅํ๊ฒ ๋๋ค.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sign Up</title>
</head>
<body>
<h1>Sign Up</h1>
<form action="/users">
Name : <input type="text" name="name">
<br>
<br>
Age : <input type="text" name="age">
<br>
<br>
<input type="submit" value="Sign Up">
</form>
</body>
</html>์ด๋ฒ์ ์ ์ html ์ ์ถ๊ฐํด ์ฌ์ฉ์ ์
๋ ฅ์ UI ๋ก ๋ฐ์๋ณด์.
form.html ์ resources.static ์ ์ถ๊ฐํ๋ฉด localhost:4567/form.html ์ ํตํด ์๋ก์ด UI ๊ฐ ์ถ๋ ฅ๋๋ค.
form.html ๋ด๋ถ์ input ํ๊ทธ๋ฅผ ํตํด ๋ฐ์ดํฐ๊ฐ ์๋ฒ๋ก ์ ๋ฌ๋๋ค.
๊ฐ ๋ธ๋ก์ ๋ฐ์ดํฐ๋ฅผ ์ง์ด๋ฃ๊ณ ๋ฒํผ์ ํด๋ฆญํ๋ฉด URL ์ด redirect ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
์ด๋ form ํ๊ทธ์ action ์ ํตํด ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ์ฃผ์๋ฅผ ๋ช
์ํ ์ ์๋ค.
์ด์ ๋ฐ์ดํฐ๋ action ์ด ๊ฐ๋ฆฌํค๋ /users ์ฃผ์๋ฅผ ๊ฐ๊ณ ์๋ main ์ get() ๋ฉ์๋์ ์ ๋ฌ๋๋ค.
์ ๋ฌ๋ name ๊ณผ age ๋ฅผ route ๋๋ค์์ ์ ์ฉ๋์ด ๊ธฐ์กด์ localhost:4567/users?name=meatsby&age=26 ๋ฅผ ํธ์ถํ๋ ํ๋ฉด์ผ๋ก ์ด๋ํ ์ ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๋ง์ฝ ํด๋น ์น์ฌ์ดํธ๊ฐ ํ์๊ฐ์ ์ ๊ดํ ์น์ฌ์ดํธ์ด๊ณ ์ ๋ ฅ๋ฐ๋ ๋ฐ์ค์ ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํ๋ค๊ณ ๊ฐ์ ํ์ ๋, url ์ ๋น๋ฐ๋ฒํธ๊ฐ ๊ทธ๋๋ก ๋ ธ์ถ๋๋ ๊ฒ์ ๋ณด์์ ๋ฌธ์ ๊ฐ ์์ ๊ฒ์ด๋ค.
์ด ๋ ์ ์ฉํ ์ ์๋ ๊ฒ์ด POST ๋ฉ์๋์ด๋ค.
POST ์ฌ์ฉ๋ฒ
public class HelloWorld {
public static void main(String[] args) {
post("/users", (req, res) -> "Hello " + req.queryParams("name") + " ๋์ด๋ " + req.queryParams("age"));
}
}post() ์ฌ์ฉ๋ฒ์ ๊ฐ๋จํ๋ค. ์์ ๋์จ ๊ฒ ์ฒ๋ผ get() ๋ฉ์๋๋ฅผ post() ๋ก ๋ณ๊ฒฝํด์ฃผ๋ฉด ๋๋ค.
์ด๋ด ๊ฒฝ์ฐ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ๋ธ๋ผ์ฐ์ ์์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํด๋ url ์ parameter ๊ฐ ๋ ธ์ถ๋์ง ์๋๋ค.
<form action="/users" method="post">๋ค๋ง ์ฃผ์ํ ์ ์ form ํ๊ทธ์ ๊ธฐ๋ณธ ๋ฉ์๋๋ get ์ด๊ธฐ ๋๋ฌธ์ post ๋ก ๋ณ๊ฒฝํด์ค์ผํ๋ค.
template engine ์ฌ์ฉ๋ฒ
template engine ์ด๋?
template engine ์ html ํ์ผ์ ๋์ ์ผ๋ก ๋ง๋ค ๋ ์ฌ์ฉํ๋ ๋๊ตฌ์ด๋ค.
localhost:4567/form.html- ์ฌ์ฉ์ ์ ๋ ฅ์ ๋ฐ๋ ์ ์ htmllocalhost:4567/users- ์ ๋ ฅ๋ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๋์ html
template engine ์ ์ฐ๋ ์ด์
public class HelloWorld {
public static void main(String[] args) {
post("/users", (req, res) -> {
return "<html>" +
"<body>" +
// ...
"</body>" +
"</html>";
}
}
}์ฌ์ฉ์์ ์
๋ ฅ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ localhost:4567/users ๋ฅผ html ๋ก ํํํ๊ณ ์ ํ๋ค.
์ด๋ฅผ ์ํํ๊ธฐ ์ํด์ ์ ์ฝ๋์ ๊ฐ์ด route ์ ๋๋ค์ ๋ฐ๋์ html ํ๊ทธ๋ฅผ ๋ฌธ์์ด๋ก ๋ฃ์ด์ฃผ๋ฉด๋๋ค.
ํ์ง๋ง ์ด๋ ๊ต์ฅํ ๋ถํธํ๊ณ ๋นํจ์จ์ ์ธ ๋ฐฉ๋ฒ์ด๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์กด์ฌํ๋ ๊ฒ์ด template engine ์ธ๋ฐ, html ํ์ผ ๊ฒฝ๋ก๋ฅผ argument ๋ก ๋ฐ์ ์ ์ฝ๋์ ๊ฐ์ ํจ๊ณผ๋ฅผ ๋ด์ค ์ ์๊ฒ ๋ง๋ค์ด ์ค๋ค.
dependency ์ ์ฉ
dependencies {
implementation "com.sparkjava:spark-template-handlebars:2.7.1"
}Spark ์์ ์ง์ํ๋ ๋ค์ํ template engine ์ค์ handlebars ๋ฅผ ์ฌ์ฉํ๋ค.
template engine ์ ํ์ฉํ๊ธฐ ์ํด build.gradle ์ ์์ ๊ฐ์ ์ฝ๋๋ฅผ ์ถ๊ฐํด์ค๋ค.
render ๋ฉ์๋ ์ถ๊ฐ
public static String render(Map<String, Object> model, String templatePath) {
return new HandlebarsTemplateEngine().render(new ModelAndView(model, templatePath));
}main ์ ์์ ๊ฐ์ ๋ฉ์๋๋ฅผ ์ถ๊ฐํด์ค ๋ค template engine ์ ํ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ถ๋ ฅํ ์ ์๋ค.
public static void main(String[] args) {
staticFiles.location("/static");
post("/users", (req, res) -> {
Map<String, Object> model = new HashMap<>();
model.put("name", req.queryParams("name"));
model.put("age", req.queryParams("age"));
return render(model, "result.html");
});
}render() ๋ฉ์๋๋ Map<String, Object> ํ์์ผ๋ก ๊ตฌ์ฑ๋์ด ์๋ model ์ ์ธ์๋ก ๋ฐ๋๋ค.
model์ key = template ์์ ์ฌ์ฉํ๋ ๋ณ์{{name}}model์ value =post์์ฒญ์ผ๋ก ๋ฐ์ ๊ฐโmeatsbyโ
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sign Up</title>
</head>
<body>
<h1>Sign Up Result</h1>
Name : {{name}}, Age : {{age}}
<br>
</body>
</html>render() ๋ฉ์๋์ ์ฌ์ฉํ result.html ์ ์์ ๊ฐ์ด ๋ง๋ค์ด ์ค๋ค.
Spark ์์ ๋์ html ํ์ผ์ ํญ์ resources.templates ๋ก ์ ๊ทผํ๊ฒ ๋ ์ค์ ๋์ด ์๊ธฐ ๋๋ฌธ์ result.html ์ ๊ผญ ํด๋น ๋๋ ํ ๋ฆฌ์ ์์น์์ผ์ผ ํ๋ค.
handlebars ๋ Mustache ๋ฌธ๋ฒ์ ์ฌ์ฉํ๋๋ฐ, {{name}} ์ model ์์ ํค๊ฐ์ด name ์ธ ๊ฐ์ ๊ฐ์ ธ์จ๋ค.
java bean ๊ณผ handlebars ๋ฐ๋ณต๋ฌธ
java bean ์ด๋?
java bean ์ด๋, ๊ธฐ๋ณธ ์์ฑ์๊ฐ ์๊ณ , iv ๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ, iv ์ ๋ํ getter ์ setter ๊ฐ ์กด์ฌํ๋ ํด๋์ค๋ค.
handlebars ๋ฐ๋ณต๋ฌธ
public static void main(String[] args) {
staticFiles.location("/static");
List<User> users = new ArrayList<>();
post("/users", (req, res) -> {
User user = new User(req.queryParams("name"), req.queryParams("age"));
users.add(user);
Map<String, Object> model = new HashMap<>();
model.put("users", users);
return render(model, "result.html");
});
}main ์ด User.java ๋ผ๋ java bean ํด๋์ค๊ฐ ์๊ธฐ๊ณ post ์์ฒญ์ผ๋ก ์ ๋ฌ๋ ์ธ์๋ค์ ํตํด user ๋ฅผ ์์ฑํ์ฌ ๋ฆฌ์คํธ์ ๋ด์ template ์ ๋๊ฒจ์ฃผ๋ ๋ฉ์๋๋ก ๋ณ๊ฒฝ๋์๋ค.
์ด๋ด ๊ฒฝ์ฐ result.html ์ ์ด๋ป๊ฒ ๋ณํด์ผ ํ ๊น?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sign Up</title>
</head>
<body>
<h1>Sign Up Result</h1>
{{#users}}
Name : {{name}}, Age : {{age}}
<br>
{{/users}}
</body>
</html>๊ฐ๋จํ๋ค. {{#users}} ์ {{/users}} ์ฝ๋ ์ฌ์ด์ ์๋ ๊ตฌ๋ฌธ์์ ์์ฐ์ค๋ฝ๊ฒ ๋ฐ๋ณต๋ฌธ์ ๋์ user ๋ฆฌ์คํธ์์ ํ๋ ์ฉ ์ถ๋ ฅํ๋ค.
๋ด๋ถ์ {{name}} ๊ณผ {{age}} ๋ ์ฌ์ค {{user.name}} ๊ณผ {{user.age}} ์ด์ง๋ง, {{#users}} ์ {{/users}} ์๊ฒ ๊ฐ์ธ์ ธ ์๊ธฐ ๋๋ฌธ์ ์๋ต์ด ๊ฐ๋ฅํ๋ค.
์ฌ๊ธฐ์ {{user.name}} ์ user.getName() ๊ณผ ๊ฐ๋ค.