์๋ ํ์ธ์, ์ผ์ด์น์ ๋๋ค.
์ค๋์ ์ฐ๋ฝ์ฒ ๊ด๋ฆฌ ํ๋ก๊ทธ๋จ์ ๋ง๋๋ ์น์ฑ ํ๋ก์ ํธ๋ฅผ ์งํํด๋ณด๋ ค๊ณ ํฉ๋๋ค.
์ฌ์ฉํ ๋๊ตฌ๋ Eclipse 2020-06 R ์ด๊ณ ์, ์คํ๋ง๋ถํธ์ ๋ถํธ์คํธ๋ฉ, ๊ทธ๋ฆฌ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๋์ mamp์ mybatis๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์ฐ์ ๊ธฐ๋ณธ์ ์ธ ๋๊ตฌ๋ค๊ณผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค ๊ทธ๋ฆฌ๊ณ ํ๋ฌ๊ทธ์ธ๋ค์ ์ค์นํด์ผํฉ๋๋ค.
๊ฐ๋ฐํ๊ฒฝ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- MacOS Catalina (10.15.6)
- Eclipse 2020-06 R (Download)
- Eclipse Spring Tool Suite 4 (์ค์นํ๊ธฐ ๊ฐ์ด๋)
- MAMP (Download) -> community ๋ฒ์ ๋ค์ด๋ก๋ ๋ฐ์ผ์ธ์, Window์ฌ์ฉ์์ ๊ฒฝ์ฐ WAMP๋ฅผ ์ค์นํ์ ์ผ ํฉ๋๋ค.
- DBeaver (๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฌด๋ฃ ํด Download) -> community ๋ฒ์ ๋ค์ด๋ก๋ ๋ฐ์ผ์ธ์
์, ์ ๋๊ตฌ๋ค์ ๋ชจ๋ ์ค์น์๋ฃํ๋ค๋ฉด ์ด์ ๋ถํฐ ์์ํด๋ณด๊ฒ ์ต๋๋ค.
์ฐ์ ๊ฒฐ๊ณผ๋ฌผ์ ์๋ ๋์์์ฒ๋ผ ๋์ฌ๊ฒ๋๋ค.
์, ๊ทธ๋ผ ์ด๋ป๊ฒ ๋ง๋๋์ง ๋ณธ๊ฒฉ์ ์ผ๋ก ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์งํ์ ์๋ ์์๋๋ก ํฉ๋๋ค.
- ํ ์ด๋ธ ์ค๊ณ
- ๋ฐฑ์๋ ๊ตฌํ
- ํ๋ก ํธ ๊ตฌํ
ํ ์ด๋ธ ์ค๊ณ
์ฐ์ ํ ์ด๋ธ ์ค๊ณ๋ถํฐ ํ๊ฒ ์ต๋๋ค. (MAMP์ค์น ๋ฐ ํ ์ด๋ธ ์์ ์ ๊ถํ ๋ฑ์ ์ค์ ์ ์ฌ๊ธฐ์ ๋ค๋ฃจ์ง ์์ต๋๋ค.)
ํ ์ด๋ธ์ contact ๋ผ๊ณ ๋ช ๋ช ์ง์ด์ฃผ๊ณ ์ปฌ๋ผ์ ์๋์ฒ๋ผ ๋ ๊ฐ๋ง ์ถ๊ฐ๋ฅผ ํด์ค ๊ฒ๋๋ค.
CREATE TABLE `contact` (
`name` varchar(100) NOT NULL,
`phone` varchar(100) DEFAULT NULL,
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
์ ๋ง ๊ฐ๋จํ ์ฐ๋ฝ์ฒ ๊ด๋ฆฌ ํ๋ก๊ทธ๋จ์ด๋ฏ๋ก ์ด๋ฆ๊ณผ ์ฐ๋ฝ์ฒ ์ ๋ณด๋ง ๋ด์ ์์ ์ด๊ณ ๊ฐ์ธ์ ๋ณด ์ํธํ ๊ฐ์๊ฑด ๋ค๋ฃจ์ง ์์ต๋๋ค.
์ ๋ ฅ๋ฐ์ ์ ๋ณด ๊ทธ๋๋ก string์ผ๋ก ์ ์ฅ์ ํ๋๋ก ํ๊ฒ ์ต๋๋ค.
์ด๋ ๊ฒ ํ ์ด๋ธ ์ค๊ณ๊ฐ ์๋ฃ๋๋ฉด ์ด์ ๋ฐฑ์๋์์ ์ฟผ๋ฆฌํด์ฌ ์ ์๋๋ก ๊ตฌํ์ ๋ค์ด๊ฐ๋ณด๊ฒ ์ต๋๋ค.
๋ฐฑ์๋ ๊ฐ๋ฐ
์ฐ์ ์๋ก์ด ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด์ผ๊ฒ ์ฃ .
์๋ ์์๋๋ก ์๋ก์ด ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด ์ค๋๋ค.
Project Explorer > New > Project ์ ํ
Wizards์์ spring์ผ๋ก ๊ฒ์ํ ๋ค Spring Starter Prject ์ ํ (์ด๊ฒ ์๋์จ๋ค๋ฉด STS์ค์นํ๊ธฐ๋ถํฐ ํ๊ณ ์ค์๋ฉด ๋ฉ๋๋ค.)
Name ํญ๋ชฉ์ ํ๋ก์ ํธ๋ช ์ ์ ๋ ฅํ๋ ๊ณณ์ ๋๋ค.
์ฌ๊ธฐ์ contact-manager ๋ผ๊ณ ๋ฃ์ด์ฃผ์๊ณ Java Version์ 8 ์ด์์ผ๋ก ์๋ฌด๊ฑฐ๋ ์ฐ์ ๋ ๋ฌด๋ฐฉํ ๊ฒ๋๋ค.
์ฌ๊ธฐ์๋ Java 11 ๋ฒ์ ์ ์ฌ์ฉํ์์ต๋๋ค.
Next๋ฅผ ๋๋ฌ ๋ค์์ผ๋ก ๋์ด๊ฐ ๋ค Available ๊ฒ์์ฐฝ์์ spring web, lombok, mybatis, mysql๋ฅผ ๊ฒ์ํ์ฌ ์ฐ์ธก์ selected ํญ๋ชฉ์ ์ธ ๊ฐ ํญ๋ชฉ์ด ๋ค์ด๊ฐ๋๋ก ์ฒดํฌํ๊ณ Finish ๋ฒํผ์ ํด๋ฆญํฉ๋๋ค. ์ ๋ ์ด๋ฏธ ์ฌ์ฉํ ์ ์ด ์์ด์ Frequently Used์ ํญ๋ชฉ์ด ๋ํ๋์ ์ฒดํฌ๋ง ํด์ฃผ์์ต๋๋ค. ์ฒ์ ํ์๋ ๋ถ๋ค์ Available์์ ๊ฒ์ํ์๋ฉด ๋ฉ๋๋ค.
์ฐ์ธก ํ๋จ์ progress bar๊ฐ ์งํ๋๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๋๋ Progress View์์ ํ์ธํ ์๋ ์์ฃ . Progress View๊ฐ ์๋ณด์ธ๋ค๋ฉด ์ดํด๋ฆฝ์ค ์๋จ ๋ฉ๋ด์์ Window > Show View > Other > Progress๋ก ๊ฒ์ํ๋ฉด ์ถ๊ฐํ ์ ์์ต๋๋ค.
์งํ์ด ์๋ฃ๋์์ผ๋ฉด Project Explorer์์ ์๋์ฒ๋ผ ํ์ผํธ๋ฆฌ๊ฐ ๋ณด์ผ๊ฒ๋๋ค.
pom.xml ํ์ผ์๋ ์๋์ ๊ฐ์ ๋ด์ฉ์ด ๋ค์ด์์ฃ .
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>contact-manager</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>contact-manager</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
์ด์ ์๋ฐ๋ก ์ฝ๋ฉํ ์ค๋น๊ฐ ๋์์ต๋๋ค.
์ฐ์ ์์์ ๋ง๋ contact ํ ์ด๋ธ์ ๋ด์ฉ์ ์กฐํํด์ค๋ API๋ฅผ ๋ง๋ค์ด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
com.example.demo ํจํค์ง ํ์์ domain ๋ผ๋ ์ด๋ฆ์ ํจํค์ง๋ฅผ ๋ง๋ค๊ณ ํด๋น ํจํค์ง์ ContactInfo.java ํ์ผ์ ์์ฑํด์ค๋๋ค.
๊ทธ๋ฆฌ๊ณ ์๋์ ๊ฐ์ ๋ด์ฉ์ ๋ฃ์ด์ค๋๋ค.
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ContactInfo {
private String name;
private String phone;
}
ContactInfo ํด๋์ค๋ contact ํ ์ด๋ธ๊ณผ ์ฐ๋ํ dto์ ๋๋ค.
com.example.demo ํจํค์ง ํ์์ mapper ๋ผ๋ ์ด๋ฆ์ ํจํค์ง๋ฅผ ๋ง๋ค๊ณ ํด๋น ํจํค์ง์ ContactInfoMapper.java ํ์ผ์ ์์ฑํด์ค๋๋ค. ContactInfoMapper๋ ํด๋์ค๊ฐ ์๋ ์ธํฐํ์ด์ค๋ก ์์ฑํด์ฃผ๊ณ ์๋์ ๊ฐ์ด ์ฟผ๋ฆฌ๋ฌธ์ ์์ฑํด์ค๋๋ค.
import com.example.demo.domain.ContactInfo;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface ContactInfoMapper {
@Select("select * from contact")
List<ContactInfo> selectAll();
@Insert("insert into contact (name, phone) values (#{name}, #{phone})")
int insert(ContactInfo contactInfo);
@Delete("delete from contact where name = #{name}")
int delete(String name);
@Select("<script>" +
"select * from contact " +
"<where>" +
"<if test=\"name != null and name.length > 0\">and name = #{name}</if>" +
"<if test=\"phone != null and phone.length > 0\">and phone = #{phone}</if>" +
"</where>" +
"</script>")
List<ContactInfo> selectBy(@Param("name") String name, @Param("phone") String phone);
@Update("update contact " +
"set phone = #{phone} " +
"where name = #{name}")
int update(String name, String phone);
}
์ด ContactInfoMapper ์ธํฐํ์ด์ค๋ ๋ง์ด๋ฐํฐ์ค์ ์ฐ๋์ด ๋์ด ์ฟผ๋ฆฌ๋ฅผ database๋ก ์ ๋ฌํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์์ค๋ ์ญํ ์ ํฉ๋๋ค.
com.example.demo ํจํค์ง ํ์์ service ๋ผ๋ ์ด๋ฆ์ ํจํค์ง๋ฅผ ๋ง๋ค๊ณ ํด๋น ํจํค์ง์ ContactInfoService.java ํ์ผ์ ์์ฑํด์ค๋๋ค.
์ด ํ์ผ์๋ ์๋์ ๊ฐ์ด ๋ด์ฉ์ ๋ฃ์ด์ฃผ์๋ฉด ๋ฉ๋๋ค.
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.demo.domain.ContactInfo;
import com.example.demo.mapper.ContactInfoMapper;
@Service
public class ContactInfoService {
@Autowired
private ContactInfoMapper contactInfoMapper;
public List<ContactInfo> getEveryContactInfo() {
return contactInfoMapper.selectAll();
}
public int addContactInfo(ContactInfo contactInfo) {
return contactInfoMapper.insert(contactInfo);
}
public int delContactInfo(String name) {
return contactInfoMapper.delete(name);
}
public List<ContactInfo> getContactInfos(String name, String phone) {
return contactInfoMapper.selectBy(name, phone);
}
public int updateContactInfo(String name, String phone) {
return contactInfoMapper.update(name, phone);
}
}
ContactInfoMapper์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ์ญํ ๋ง ํ๊ณ ์์ง๋ง ํ๋ก์ ํธ๊ฐ ๋ณต์กํด์ง๋ฉด ๊ฐ์ข ๋น์ฆ๋์ค ๋ก์ง์ด ๋ค์ด๊ฐ์ผํ ํด๋์ค์ ๋๋ค.
์กฐํํ ๋ค์ ์ ๋ ฌ์ ํ๋ค๋ ์ง, ํํฐ๋ง์ ํ๋ค๋ ์ง ํ๋ ๋ก์ง์ ์ด ์๋น์ค ํด๋์ค์ ๋ฃ์ด์ฃผ์๋ฉด ๋ฉ๋๋ค.
์ด์ controller์ชฝ์์ ์ฌ์ฉํ ์๋ต ๋๋ฉ์ธ ํด๋์ค๋ฅผ ํ๋ ๋ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค.
com.example.demo.domain ํจํค์ง์ Response.java ํ์ผ์ ์์ฑํ๊ณ ์๋์ ๊ฐ์ด ์์ฑํด์ค๋๋ค.
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@JsonInclude(Include.NON_NULL)
public class Response<T> {
private int code;
private String message;
private T data;
public Response(int code, String message) {
this.code = code;
this.message = message;
}
public Response(int code, String message, T data) {
this(code, message);
this.data = data;
}
}
๋ง์ง๋ง์ผ๋ก com.example.demo ํจํค์ง ํ์์ controller ๋ผ๋ ์ด๋ฆ์ ํจํค์ง๋ฅผ ๋ง๋ค๊ณ ํด๋น ํจํค์ง์ ContactInfoController.java ํ์ผ์ ์์ฑํด์ ์๋ ๋ด์ฉ์ ๋ฃ์ด์ฃผ์ธ์.
import com.example.demo.domain.ContactInfo;
import com.example.demo.domain.Response;
import com.example.demo.service.ContactInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/contact-info")
public class ContactInfoController {
@Autowired
private ContactInfoService contactInfoService;
@GetMapping("/list") // --> localhost:8080/contact-info/list
public List<ContactInfo> getAllContactInfo() {
return contactInfoService.getEveryContactInfo();
}
@GetMapping("/get")
public ResponseEntity<Response> getContactInfos(
@RequestParam(required = false) String name,
@RequestParam(required = false) String phone) {
List<ContactInfo> result = contactInfoService.getContactInfos(name, phone);
if (result.size() == 0) {
return ResponseEntity.status(HttpStatus.OK).body(new Response(204, "๋ฐ์ดํฐ ์์"));
}
return ResponseEntity.ok(new Response(200, "์กฐํ ์ฑ๊ณต", result));
}
@PostMapping("/add")
public ResponseEntity<Response> addNewContactInfo(@RequestBody ContactInfo contactInfo) {
contactInfoService.addContactInfo(contactInfo);
return ResponseEntity.ok(new Response(200, "๋ฑ๋ก ์ฑ๊ณต"));
}
@DeleteMapping("/del")
public ResponseEntity<Response> delContactInfo(@RequestParam String name) {
int result = contactInfoService.delContactInfo(name);
if (result == 0) {
return ResponseEntity.status(HttpStatus.OK).body(new Response(204, "๋ฐ์ดํฐ ์์"));
}
return ResponseEntity.ok(new Response(200, "์ญ์ ์ฑ๊ณต"));
}
@PutMapping("/update")
public ResponseEntity<Response> updateContactInfo(@RequestParam String name, @RequestParam String phone) {
int result = contactInfoService.updateContactInfo(name, phone);
if (result == 0) {
return ResponseEntity.status(HttpStatus.OK).body(new Response(204, "๋ฐ์ดํฐ ์์"));
}
return ResponseEntity.ok(new Response(200, "์์ ์ฑ๊ณต"));
}
@ExceptionHandler
public ResponseEntity<Response> contactInfoErrorHandler(Exception e) {
log.error("error!!, {}", e.getClass().getName(), e);
if (e instanceof DuplicateKeyException) {
return ResponseEntity.status(HttpStatus.CONFLICT).body(new Response(409, "์ด๋ฏธ ์ค๋ณต๋ ์ด๋ฆ์ด ๋ฑ๋ก๋์ด์์ต๋๋ค."));
} else {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new Response(500, "์๋ฒ์ค๋ฅ๊ฐ ๋ฐ์ํ์์ต๋๋ค."));
}
}
}
์ด ํด๋์ค๊ฐ ํ๋ก ํธ์ ๋ฐฑ์๋๋ฅผ ์ฐ๊ฒฐํด์ฃผ๋ ๋ถ๋ถ์ผ๋ก API url์ ์ ์ํ๊ณ ์ด๋ค ํ๋ผ๋ฏธํฐ๋ค์ ์ ๋ฌ๋ฐ์์ผ ํ๋์ง, ์๋ต์ ์ด๋ป๊ฒ ์ฃผ๊ณ ์์ธ์ฒ๋ฆฌ๋ ์ด๋ป๊ฒ ํ๋์ง์ ๋ํ ๋ด์ฉ์ด ๋ด๊ฒจ์์ฃ .
์ด์ ๋ฐฑ์๋ ๊ตฌํ์ ์๋ฃ๋์์ต๋๋ค. ์ ์ฐธ!! ํ๋ ๋นผ๋จน์๊ฒ ์๋ค์. database ์ ์ ์ ๋ณด๋ฅผ ๋นผ๋จน์์ต๋๋ค. ์, src/main/resources ํ์์ application.properties ํ์ผ์ ์ด์ด์ ์ง์ ์์ฑํ์ จ๋ database ์ ์ ์ ๋ณด๋ฅผ ์๋์ฒ๋ผ ๋ฃ์ด์ฃผ์ธ์.
spring.datasource.url=jdbc:mysql://localhost:8889/javastudy?useSSL=false&serverTimezone=UTC
spring.datasource.username=javastudy
spring.datasource.password=javastudy
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
์ ์ ํฌํธ(8889)์ database๋ช (javastudy) ๊ทธ๋ฆฌ๊ณ username, password ์ ๋ณด๋ฅผ ์์ฑํ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ง๊ฒ ๋ณ๊ฒฝํด์ฃผ์ ์ผ ํฉ๋๋ค.
์ฌ๊ธฐ๊น์ง ์๋ฃ๋์๋ค๋ฉด Project Explorer์ ์๋์ ๊ฐ์ด ํ์ผ๋ค์ด ์กด์ฌํด์ผ ํฉ๋๋ค.
์ด์ ContactManagerApplication์ Sppring Boot App์ผ๋ก ๊ธฐ๋ํด์ API๊ฐ ์ ๋์ํ๋์ง ํ์ธ์ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์ฑ ๊ธฐ๋์ ์ฑ๊ณตํ๋ฉด Console View์์ ์๋ ๋ก๊ทธ๋ฅผ ํ์ธ ํ ์ ์์ต๋๋ค.
INFO 69998 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
INFO 69998 --- [main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
INFO 69998 --- [main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.37]
INFO 69998 --- [main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
INFO 69998 --- [main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1185 ms
INFO 69998 --- [main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
INFO 69998 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
INFO 69998 --- [main] c.e.demo.ContactManagerApplication : Started ContactManagerApplication in 2.457 seconds (JVM running for 3.521)
์ด์ ๋ธ๋ผ์ฐ์ ์ฐฝ์ ์ด๊ณ http://localhost:8080/contact-info/get ์ ์ฃผ์์ฐฝ์ ์ ๋ ฅํ๊ณ ํธ์ถํด๋ณด๊ฒ ์ต๋๋ค.
์๋์ฒ๋ผ ์ถ๋ ฅ์ด ๋๋ฉด ์ ์์ ์ผ๋ก ์ ๋์ํ ๊ฒ๋๋ค.
{"code":204,"message":"๋ฐ์ดํฐ ์์"}
์ฌ๊ธฐ์ ํน์ ์๋ฌ๊ฐ ๋๋ค๋ฉด ์๋ก ์ฌ๋ผ๊ฐ์ ๋ค์ ์์๋๋ก ๋ฐ๋ผํด๋ณด์ธ์.
๊ทธ๋๋ ์๋์ ๋ค๋ฉด....๋๊ธ๋ก ์๋ฌ๋ฉ์์ง๋ฅผ ๋จ๊ฒจ์ฃผ์๋ฉด ํจ๊ป ๊ณ ๋ฏผํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์, ์ด์ ํ๋ก ํธ ๊ฐ๋ฐ๋ก ๋์ด๊ฐ๋๋ค.
ํ๋ก ํธ ๊ฐ๋ฐ
ํ๋ก ํธ ์์ญ ๊ฐ๋ฐ์ ์ํด์ pom.xml์ ๋ ๊ฐ์ dependency๋ฅผ ์ถ๊ฐํด์ฃผ๊ฒ ์ต๋๋ค.
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>4.5.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
thymeleaf์ ๋ถํธ์คํธ๋ฉ์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ controller ํจํค์ง ๋ด์ ViewController.java ํ์ผ์ ๋ง๋ค์ด์ ์๋์ ๊ฐ์ด ์งง์ ์ฝ๋๋ฅผ ์์ฑํด์ฃผ๊ฒ ์ต๋๋ค.
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class ViewController {
@GetMapping("/contact-info")
public String contactInfo() {
return "contact-info"; // contact-info.html์ ํ๋ฉด์ ๋ณด์ฌ์ฃผ๋ ์ญํ
}
}
๊ทธ๋ฆฌ๊ณ src/main/resources ํ์์ templates ๋๋ ํ ๋ฆฌ์ contact-info.html ํ์ผ์ ํ๋ ์์ฑํ๊ณ ์๋ ๋ด์ฉ์ ๋ฃ์ด์ฃผ์ธ์.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- JS link -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="/webjars/bootstrap/4.5.0/js/bootstrap.min.js"></script>
<script src="script/contact-info.js"></script>
<!-- CSS link -->
<link rel="stylesheet"
href="/webjars/bootstrap/4.5.0/css/bootstrap.min.css"/>
<link rel="stylesheet" href="style/contact-info.css"/>
</head>
<body>
<div id="content">
<h1>์ฐ๋ฝ์ฒ ๊ด๋ฆฌ</h1>
<p>
<label>์ด๋ฆ:</label>
<input id="name" type="text" placeholder="์ด๋ฆ" pattern=""/>
<label>์ฐ๋ฝ์ฒ:</label>
<input id="phone" type="text" placeholder="010-1234-5678"/>
<button type="button" class="btn btn-primary btn-lg" onclick="addContact()">๋ฑ๋ก</button>
<button type="button" class="btn btn-danger btn-lg" onclick="delContact()">์ญ์ </button>
</p>
<div>
<button type="button" class="btn btn-info btn-lg" onclick="getContacts()">์ฐ๋ฝ์ฒ ๋ชฉ๋ก ์กฐํ</button>
<table>
<thead>
<tr>
<th class="name">Name</th>
<th class="phone">Phone</th>
</tr>
</thead>
<tbody id="contact-info-table"></tbody>
</table>
</div>
</div>
<!-- Update Modal -->
<div class="modal fade" id="updateModal" tabindex="-1" role="dialog" aria-labelledby="updateModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="updateModalLabel">์ฐ๋ฝ์ฒ ์์ </h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" onclick="updateContactInfo()">Save changes</button>
</div>
</div>
</div>
</div>
</body>
</html>
๊ทธ๋ฆฌ๊ณ src/main/resources ํ์์ static ๋๋ ํ ๋ฆฌ ํ์์ style, script ๋ ๊ฐ์ ๋๋ ํ ๋ฆฌ๋ฅผ ์์ฑํด์ค๋๋ค.
style ๋๋ ํ ๋ฆฌ์๋ contact-info.css ํ์ผ์ ๋ฃ์๊ฑฐ๊ณ , script ๋๋ ํ ๋ฆฌ์๋ contact-info.js ํ์ผ์ ๋ฃ์ ๊ฒ๋๋ค.
๊ฐ ํ์ผ์ ๋ด์ฉ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
src/main/resources/static/style/contact-info.css
body {background-color: beige;}
th {text-align:center}
.name {width:100px;}
.phone {width:200px;}
#content {margin:100px;}
#contact-info-table tr:hover {background-color: cornflowerblue;}
src/main/resources/static/script/contact-info.js
$( document ).ready(function() {
getContacts();
});
function addContact() {
let name = $('#name').val();
if (!name) {
alert('"name" is required!');
return;
}
if (!validateName(name)) {
alert('"name" is invalid! ํ๊ธ๋ง ์
๋ ฅ๊ฐ๋ฅํฉ๋๋ค.');
return;
}
let phone = $('#phone').val();
if (!validatePhone(phone)) {
alert('"phone number" is invalid!\nvalid format: 000-0000-0000');
return;
}
let contactInfo = new Object();
contactInfo.name = name;
contactInfo.phone = phone;
$.ajax({
url: 'contact-info/add',
dataType: 'json',
type: 'post',
contentType: 'application/json',
data: JSON.stringify(contactInfo)
}).done(function(response) {
alert(response.message);
console.log(response);
$('#name').val('');
$('#phone').val('');
getContacts();
}).fail(function(jqXHR, textStatus, errorThrown) {
alert(jqXHR.responseText);
console.log(jqXHR.responseText);
});
}
function validateName(name) {
return /^[๊ฐ-ํฃ]+$/.test(name);
// /^[A-Za-z]+$/
}
function validatePhone(phone){
return /^\d{2,3}-\d{3,4}-\d{4}$/.test(phone);
}
function delContact() {
let name = $('#name').val();
if (!name) {
alert('"name" is required!');
return;
}
$.ajax({
url: 'contact-info/del?name=' + name,
dataType: 'json',
type: 'delete',
contentType: 'application/json'
}).done(function(response) {
alert(response.message);
console.log(response);
$('#name').val('');
$('#phone').val('');
getContacts();
}).fail(function(jqXHR, textStatus, errorThrown) {
alert(jqXHR.responseText);
console.log(jqXHR.responseText);
});
}
function getContacts() {
let name = $('#name').val();
let phone = $('#phone').val();
$.ajax({
url: 'contact-info/get' + generateQueryParams(name,phone),
dataType: 'json',
type: 'get',
contentType: 'application/json'
}).done(function(response) {
printContactInfos(response.data);
}).fail(function(jqXHR, textStatus, errorThrown) {
alert(jqXHR.responseText);
console.log(jqXHR.responseText);
});
}
function generateQueryParams(name, phone) {
let params = '';
if (name || phone) {
params = '?';
if (name) {
params += 'name=' + name;
}
if (phone) {
if (params.length > 1) { params += '&';}
params += 'phone=' + phone;
}
}
return params;
}
function printContactInfos(contactInfos) {
let rows = '';
if (contactInfos) {
contactInfos.forEach(function (item, index) {
rows += '<tr onclick="popsUpUpdateModal(this)" ><td class="name">' + item.name
+ '</td><td class="phone">' + item.phone + '</td></tr>';
});
} else {
alert('์กฐํ๋ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.');
}
$('#contact-info-table').html(rows);
}
function popsUpUpdateModal(row) {
let name = $(row).find('.name').text();
let phone = $(row).find('.phone').text();
let modalBody = '<input type="text" class="name" value="' + name + '" disabled>'
+ '<input type="text" class="phone" value="' + phone + '">';
$('#updateModal .modal-body').html(modalBody);
$('#updateModal').modal('show');
}
function updateContactInfo() {
let name = $('#updateModal .modal-body .name').val();
let phone = $('#updateModal .modal-body .phone').val();
if (!validateName(name)) {
alert('"name" is invalid! ํ๊ธ๋ง ์
๋ ฅ๊ฐ๋ฅํฉ๋๋ค.');
return;
}
if (!validatePhone(phone)) {
alert('"phone number" is invalid!\nvalid format: 000-0000-0000');
return;
}
$.ajax({
url: 'contact-info/update' + generateQueryParams(name,phone),
dataType: 'json',
type: 'put',
contentType: 'application/json'
}).done(function(response) {
alert(response.message);
console.log(response);
getContacts();
$('#updateModal').modal('hide');
}).fail(function(jqXHR, textStatus, errorThrown) {
alert(jqXHR.responseText);
console.log(jqXHR.responseText);
});
}
๊ฐ ํ์ผ๋ค ๋ด์์ ํ๋ ์ญํ ์ด ๋ญ์ง ์ ๋ชจ๋ฅด๊ฒ ๋ค๋ ๋ถ๋ค์ ๋๊ธ ๋ฌ์์ฃผ์๋ฉด ์๋ ํ๋ ๋ด์์ ๋ต๋ณ ๋ฌ์๋๋ฆด๊ฒ์.
ํฌ์คํ ์ด ๋๋ฌด ๊ธธ์ด์ ธ์ ์ผ์ผ์ด ์ค๋ช ์ ๋ค ์ถ๊ฐํ ์๊ฐ ์์์ ์ํด๋ฐ๋๋๋ค.
์, ์ด๋ ๊ฒ ์ด 4 ๊ฐ์ ํ์ผ์ ์ถ๊ฐํ๋ ๊ฒ๋ง์ผ๋ก ํ๋ก ํธ ๊ฐ๋ฐ์ด ์๋ฃ๋์์ต๋๋ค.
์ด์ ๋ค์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ๊ธฐ๋ํ ๋ค ํ๋ฉด์ผ๋ก ์ ์ํด์ ๊ธฐ๋ฅ๋ค์ ์ฌ์ฉํด ๋ณด๊ฒ ์ต๋๋ค.
์ด๋ฒ์๋ localhost:8080/contact-info ์ฃผ์๋ก ์ ์์ ํ์๋ฉด ๋ฉ๋๋ค. ์์์ ์ฌ์ฉํ๋ /get ์ฃผ์๋ API๋ฅผ ์ง์ ํธ์ถํ๋ ๊ฒ์ด๋ ๋นผ๊ณ ์ ์ํด์ฃผ์๋ฉด ์๋์ฒ๋ผ ํ๋ฉด์ด ๋ฐ๊ฒ๋๋ค.
์ด ์น์ฑ์์๋ ๋ฑ๋ก์์ ์ด๋ฆ์ ์ ์์ ์ธ ํ๊ธ์ ์ ๋ ฅํด์ค์ผ ํ๊ณ ์ฐ๋ฝ์ฒ์ ๊ฒฝ์ฐ -๋ฅผ ํฌํจํ์ฌ ์๋ฆฌ์๋ฅผ ๊ฒ์ฆํ๋๋ก ๋์ด์์ต๋๋ค.
๊ฒ์ฆ์ contact-info.js์์ validateName(), validatePhone() ํจ์์์ ์ฒ๋ฆฌํ๊ณ ์์ผ๋ ํ์ธํด๋ณด์๊ธฐ ๋ฐ๋๋๋ค.
์ค๋ฅ๋ฐ์์์๋ ํ๋ฉด์ ์ค๋ฅ๋ฐ์ํ๋ค๊ณ ๊ฒฝ๊ณ ํ์ ์ฐฝ์ด ๋จ๋๋ก ๋์ด์์ผ๋ฉฐ, ์๋ฒ๋ก๊ทธ๋ฅผ ํตํด ํ์ธ์ด ๊ฐ๋ฅํ๋๋ก ๋์ด์์ต๋๋ค.
๋ํ ์ฐ๋ฝ์ฒ ์์ ์ ์ ์ผ ์ฒ์์ ๊ณต์ ํด๋๋ฆฐ ๋์์์ ๋ฐ๋ผํด์ฃผ์๋ฉด ๋ฉ๋๋ค.
์ฌ๊ธฐ๊น์ง 30๋ถ๋ง์ ์ฐ๋ฝ์ฒ ๊ด๋ฆฌ ์น์ฑ ๋ง๋ค๊ธฐ ํฌ์คํ ์ ๋ง์น๋๋ก ํ๊ฒ ์ต๋๋ค.
๊ถ๊ธํ์ ์ฌํญ์ด๋ ์ ์๋์๋ ๋ถ๋ค์ ๋๊ธ ๋ฌ์์ฃผ์๋ฉด ํ์ธํด์ ๋ต๋ณ๋๋ฆฌ๋๋ก ํ๊ฒ ์ต๋๋ค.
์ค๋๋ ์ฆํํ์ธ์~
'๐ป Programming > ์นํ๋ก๊ทธ๋๋ฐ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[HTML] <fieldset> ํ๊ทธ (0) | 2020.11.20 |
---|---|
Eclipse + SpringBoot + JSP ๊ฐ๋ฐํ๊ฒฝ ์ธํ ํ๊ธฐ #2 (0) | 2020.07.23 |
Eclipse + SpringBoot + JSP ๊ฐ๋ฐํ๊ฒฝ ์ธํ ํ๊ธฐ #1 (3) | 2020.07.22 |
[MySQL] ๋ฐ๋ชฌํ๋ก์ธ์ค๊ฐ ์๊พธ ์๋์ผ๋ก ์คํ๋์ (0) | 2020.07.05 |
[MySQL] auto_increment ์ปฌ๋ผ ๊ฐ์ ์ธ์ ์ฆ๊ฐํ ๊น (0) | 2020.07.02 |