일단 처음은 스프링의 구조에 대해 알아야 이해가 쉬울 것이라 생각해 사진을 첨부한다.
사진을 보면 컨트롤러는 서비스에 종속되어 있고, 서비스는 리포지토리에 종속되어 있다.
⭐⭐따라서 패키지를 우선적으로 controller, service, repository 3개로 구성한다.
package com.codingbox.core2.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.codingbox.core2.dto.Member;
import com.codingbox.core2.dto.MemberForm;
import com.codingbox.core2.service.MemberService;
@Controller
public class MemberController {
//controller가 서비스에 의존한다고 표현한다. 그래서 서비스로 객체를 만든것
//MemberService mService = new MemberService();
// Service는 여러 컨트롤러에서 가져다 쓸 수 있기 때문에
//Spring container에 등록을 해야한다.
//스프링의 장점을 이용해서 작업하자. 의존성 주입 작업
private final MemberService memberService; //final은 수정 불가능하게 못 박아버리는 역할
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
@GetMapping("/members/new")
public String createForm() {
return "members/createMemberForm";
}
@PostMapping("/members/new")
public String create(MemberForm form) {
Member member = new Member();
member.setName(form.getName());
memberService.join(member);
//회원가입 등록을 한 후에
//홈 화면으로 돌린다.
return "redirect:/";
}
// / members,getMapping
//list(Model model),return "members/memberList"
@GetMapping("/members")
public String list(Model model) {
List<Member> members = memberService.findMembers();
model.addAttribute("members",members);
return "members/memberList";
}
}
이 부분에서 Autowired 어노테이션을 통해 서비스에 종속됨을 보여주고 있다.
웰컴페이지이다. 위의 코드를 보면 회원 가입의 경우 /members/new을 매핑하고, 회원 목록의 경우 /members을 매핑하고 있다.
이렇게 매핑이 잘 되어 있다.
package com.codingbox.core2.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.codingbox.core2.dto.Member;
import com.codingbox.core2.repository.MemberRepository;
import com.codingbox.core2.repository.MemoryMemberRepository;
import jakarta.transaction.Transactional;
@Service
@Transactional
public class MemberService {
//서비스는 리포지토리에 의존. 그래서 리포지토리 객체를 만든다.
//MemoryMemberRepository memberRepository = new MemoryMemberRepository();
//스프링스러운 의존성 주입
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
//회원가입
public int join(Member member) {
memberRepository.save(member);
return member.getId();
}
//전체 회원 조회
public List<Member> findMembers(){
return memberRepository.findAll();
}
}
⭐서비스는 리포지토리에 종속됨을 확인할 수 있다.
리포지토리에서 멤버변수를 저장하고, 전체 회원을 조회한다.
👊 여기서부터가 중요한데, jdbc를 이용하여 구현하는 방법과 jpa를 사용하여 보다 편리하게 repository를 구현하는 방식으로 나누어 본다.
일단 인터페이스로 save, findall 지정
package com.codingbox.core2.repository;
import java.util.List;
import com.codingbox.core2.dto.Member;
public interface MemberRepository {
//회원 저장
Member save(Member member);
//전체 조회
List<Member>findAll();
}
package com.codingbox.core2.repository;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.stereotype.Repository;
import com.codingbox.core2.dto.Member;
@Repository
public class JdbcMemberRepository implements MemberRepository{
private final DataSource dataSource;
public JdbcMemberRepository(DataSource dataSource) {
this.dataSource = dataSource;
}
@Override
public Member save(Member member) {
String sql = "INSERT INTO MEMBER values(member_seq.nextval,?)";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
String generatedColumns[]= {"ID"};
pstmt = conn.prepareStatement(sql,generatedColumns);
pstmt.setString(1, member.getName());
pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys();
if(rs.next()) {
member.setId(rs.getInt(1));
}
} catch (Exception e) {
e.printStackTrace();
}
return member;
}
@Override
public List<Member> findAll() {
String sql = "SELECT * FROM MEMBER";
Connection conn = null;
PreparedStatement pstm = null;
ResultSet rs = null;
List<Member> members = new ArrayList<>();
try {
conn = dataSource.getConnection();
pstm = conn.prepareStatement(sql);
rs = pstm.executeQuery();
while(rs.next()) {
Member member = new Member();
member.setId(rs.getInt("id"));
member.setName(rs.getString("name"));
members.add(member);
}
} catch (Exception e) {
}
return members;
}
}
어노테이션으로 리포지토리를 달았고 sql쿼리문을 그대로 복사 붙여넣기 해주는 마치 jsp와 같은 방식을 취하여 DB에 접근한다.(jpa로 구현할 땐 이 어노테이션을 제거해야 한다.)
save 같은 경우 회원가입이기에 데이터를 넣는 행위이다. 따라서 insert문이 들어간다.
findAll은 회원목록이기에 select문을 활용한다.
member는 배열로서 받아온다.
package com.codingbox.core2.repository;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.codingbox.core2.dto.Member;
import jakarta.persistence.EntityManager;
@Repository
public class JpaMemberRepository implements MemberRepository {
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
@Override
public Member save(Member member) {
em.persist(member);
return member;
}
@Override
public List<Member> findAll() {
return em.createQuery("select m from Member m",Member.class).getResultList();
}
}
중요한 것에 비해 생각보다 코드가 간소하다.
하나하나 뜯어보면, JpaMemberRepository도 MemberRepository를 취하며,public JpaMemberRepository(EntityManager em) {
this.em = em;
}
즉 리포지토리 자체에 의존성을 둔다.
⭐save를 보면, 스프링 자체에서 save를 해주고 별다른 예외 처리가 필요없다.
⭐findAll 도 상당히 간소화 되었다.
<!DOCTYPE html>
<html>
<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<table>
<thead>
<tr>
<th>no</th>
<th>이름</th>
</tr>
</thead>
<tbody>
<tr th:each="member : ${members}">
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
</tr>
</tbody>
</table>
</body>
</html>
${members}이 부분이 멤버가 연결되는 부분이다. 컨트롤러에서의 model.addAttribute("members",members); 이 코드와 연결된다.
추가적인 세팅