diff --git a/build.gradle b/build.gradle index 07d2578..89689ec 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,8 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' + //implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' diff --git a/sql/ddl.sql b/sql/ddl.sql new file mode 100644 index 0000000..83fd1dd --- /dev/null +++ b/sql/ddl.sql @@ -0,0 +1,18 @@ +drop table if exists member CASCADE; + create table member + ( + id bigint generated by default as identity, + name varchar(255), + primary key (id) + ); + + + drop table if exists item CASCADE; + create table item + ( + id bigint generated by default as identity, + name varchar(255), + price integer not null, + count integer not null, + primary key (id) + ); \ No newline at end of file diff --git a/src/main/java/landvibe/springintro/aop/TimeTraceAop.java b/src/main/java/landvibe/springintro/aop/TimeTraceAop.java new file mode 100644 index 0000000..027bf8a --- /dev/null +++ b/src/main/java/landvibe/springintro/aop/TimeTraceAop.java @@ -0,0 +1,25 @@ +package landvibe.springintro.aop; + +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.springframework.stereotype.Component; + +@Aspect +@Component //스프링 빈에 등록 +public class TimeTraceAop { + @Around("execution(* landvibe.springintro..*(..))") //이 로직을 적용하고자 하는 범위 설정 + public Object execute(ProceedingJoinPoint joinPoint) throws Throwable { + long start = System.currentTimeMillis(); + System.out.println("START: " + joinPoint.toString()); + + try { + return joinPoint.proceed(); + } finally { + long finish = System.currentTimeMillis(); + long timeMs = finish - start; + + System.out.println("END: " + joinPoint.toString() + " " + timeMs + "ms"); + } + } +} diff --git a/src/main/java/landvibe/springintro/controller/HelloController.java b/src/main/java/landvibe/springintro/controller/HelloController.java new file mode 100644 index 0000000..aaa070c --- /dev/null +++ b/src/main/java/landvibe/springintro/controller/HelloController.java @@ -0,0 +1,48 @@ +package landvibe.springintro.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +@Controller +public class HelloController { + @GetMapping("hello") + public String hello(Model model) { + model.addAttribute("data", "LandVibe"); + return "hello"; + } + + @GetMapping("hello-mvc") + public String helloMvc( + @RequestParam(value = "name", required = false, defaultValue = + "landVibe") String name, + Model model + ) { + model.addAttribute("name", name); + return "hello-template"; + } + + @GetMapping("hello-string") + @ResponseBody + public String helloString(@RequestParam("name") String name) { + return "hello " + name; + } + + @GetMapping("hello-api") + @ResponseBody + public Hello helloApi(@RequestParam("name") String name) { + Hello hello = new Hello(); + hello.setName(name); + return hello; + } + static class Hello { + private String name; + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + } +} diff --git a/src/main/java/landvibe/springintro/controller/HomeController.java b/src/main/java/landvibe/springintro/controller/HomeController.java new file mode 100644 index 0000000..6987642 --- /dev/null +++ b/src/main/java/landvibe/springintro/controller/HomeController.java @@ -0,0 +1,17 @@ +package landvibe.springintro.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +@Controller +public class HomeController { + @GetMapping("/") //localhost:8080으로 들어오면 아래 호출 + public String home() { + return "home"; //home.html이 호출됨 + } +} + +/* +기존에 있던 index.html 말고 home.html이 보여지는 이유는 +톰캣 서버에 요청이 오면 먼저 스프링 컨테이너에서 해당되는 컨트롤러가 있는지를 우선적으로 확인. +따라서, 컨트롤러가 있으므로 index.html은 호출되지 않고 종료. + */ \ No newline at end of file diff --git a/src/main/java/landvibe/springintro/item/config/ItemConfig.java b/src/main/java/landvibe/springintro/item/config/ItemConfig.java new file mode 100644 index 0000000..357b5eb --- /dev/null +++ b/src/main/java/landvibe/springintro/item/config/ItemConfig.java @@ -0,0 +1,45 @@ +package landvibe.springintro.item.config; + +import landvibe.springintro.item.repository.ItemRepository; +import landvibe.springintro.item.repository.JpaItemRepository; +import landvibe.springintro.item.service.ItemService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; + +import javax.sql.DataSource; +import jakarta.persistence.EntityManager; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ItemConfig { + /* + private final DataSource dataSource; + private final EntityManager em; + + @Autowired + public ItemConfig(DataSource dataSource, EntityManager em) { + this.dataSource = dataSource; + this.em = em; + } + */ + private final ItemRepository itemRepository; + + public ItemConfig(ItemRepository itemRepository) { + this.itemRepository = itemRepository; + } + + @Bean + public ItemService itemService() { + return new ItemService(itemRepository); + } + + /* + @Bean + public ItemRepository itemRepository() { + // return new MemoryItemRepository(); + // return new JdbcItemRepository(dataSource); + // return new JdbcTemplateItemRepository(dataSource); + return new JpaItemRepository(em); + } + */ +} diff --git a/src/main/java/landvibe/springintro/item/controller/ItemController.java b/src/main/java/landvibe/springintro/item/controller/ItemController.java new file mode 100644 index 0000000..32266b9 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/controller/ItemController.java @@ -0,0 +1,46 @@ +package landvibe.springintro.item.controller; + +import landvibe.springintro.item.service.ItemService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.ui.Model; +import landvibe.springintro.item.domain.Item; + +import java.util.List; + +@Controller +public class ItemController { + private final ItemService itemService; + + @Autowired + public ItemController(ItemService itemService) { + this.itemService = itemService; + } + + @GetMapping("/items/new") + public String createForm(){ + return "items/createForm"; + } + + @PostMapping("/items/new") + public String create(@ModelAttribute ItemCreateForm form) { + Item item = new Item(); + item.setName(form.getName()); + item.setPrice(form.getPrice()); + item.setCount(form.getCount()); + itemService.create(item); + + return "redirect:/"; + } + + @GetMapping("/items") + public String list(Model model){ + List items = itemService.findItems(); + model.addAttribute("items", items); + + return "items/itemList"; + } +} \ No newline at end of file diff --git a/src/main/java/landvibe/springintro/item/controller/ItemCreateForm.java b/src/main/java/landvibe/springintro/item/controller/ItemCreateForm.java new file mode 100644 index 0000000..77f3978 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/controller/ItemCreateForm.java @@ -0,0 +1,31 @@ +package landvibe.springintro.item.controller; + +public class ItemCreateForm { + private String name; + private Integer price; + private Integer count; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getPrice() { + return price; + } + + public void setPrice(Integer price) { + this.price = price; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } +} diff --git a/src/main/java/landvibe/springintro/item/domain/Item.java b/src/main/java/landvibe/springintro/item/domain/Item.java new file mode 100644 index 0000000..83a7e2b --- /dev/null +++ b/src/main/java/landvibe/springintro/item/domain/Item.java @@ -0,0 +1,48 @@ +package landvibe.springintro.item.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity +public class Item { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + private Integer price; + private Integer count; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getPrice() { + return price; + } + + public void setPrice(Integer price) { + this.price = price; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } +} diff --git a/src/main/java/landvibe/springintro/item/repository/ItemRepository.java b/src/main/java/landvibe/springintro/item/repository/ItemRepository.java new file mode 100644 index 0000000..fd8f73e --- /dev/null +++ b/src/main/java/landvibe/springintro/item/repository/ItemRepository.java @@ -0,0 +1,14 @@ +package landvibe.springintro.item.repository; + +import landvibe.springintro.item.domain.Item; +import java.util.List; +import java.util.Optional; +public interface ItemRepository { + Item save(Item item); + + Optional findById(Long id); + + Optional findByName(String name); + + List findAll(); +} diff --git a/src/main/java/landvibe/springintro/item/repository/JdbcItemRepository.java b/src/main/java/landvibe/springintro/item/repository/JdbcItemRepository.java new file mode 100644 index 0000000..a82856a --- /dev/null +++ b/src/main/java/landvibe/springintro/item/repository/JdbcItemRepository.java @@ -0,0 +1,175 @@ +package landvibe.springintro.item.repository; + +import landvibe.springintro.item.domain.Item; +import org.springframework.jdbc.datasource.DataSourceUtils; + +import javax.sql.DataSource; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class JdbcItemRepository implements ItemRepository { + private final DataSource dataSource; + + public JdbcItemRepository(DataSource dataSource) { + this.dataSource = dataSource; + } + + @Override + public Item save(Item item) { + String sql = "insert into item(name, price, count) values(?, ?, ?)"; + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + + try { + conn = getConnection(); + pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + + pstmt.setString(1, item.getName()); + pstmt.setInt(2, item.getPrice()); + pstmt.setInt(3, item.getCount()); + + pstmt.executeUpdate(); + rs = pstmt.getGeneratedKeys(); + + if (rs.next()) { + item.setId(rs.getLong(1)); + } else { + throw new SQLException("id 조회 실패"); + } + + return item; + } catch (Exception e) { + throw new IllegalStateException(e); + } finally { + close(conn, pstmt, rs); + } + } + + @Override + public Optional findById(Long id) { + String sql = "select * from item where id = ?"; + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + + try { + conn = getConnection(); + pstmt = conn.prepareStatement(sql); + pstmt.setLong(1, id); + + rs = pstmt.executeQuery(); + + if (rs.next()) { + Item item = new Item(); + item.setId(rs.getLong("id")); + item.setName(rs.getString("name")); + item.setCount(rs.getInt("count")); + item.setPrice(rs.getInt("price")); + + return Optional.of(item); + } else { + return Optional.empty(); + } + } catch (Exception e) { + throw new IllegalStateException(e); + } finally { + close(conn, pstmt, rs); + } + } + + @Override + public Optional findByName(String name) { + String sql = "select * from item where name = ?"; + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + try { + conn = getConnection(); + pstmt = conn.prepareStatement(sql); + pstmt.setString(1, name); + rs = pstmt.executeQuery(); + + if (rs.next()) { + Item item = new Item(); + item.setId(rs.getLong("id")); + item.setName(rs.getString("name")); + item.setCount(rs.getInt("count")); + item.setPrice(rs.getInt("price")); + return Optional.of(item); + } + + return Optional.empty(); + } catch (Exception e) { + throw new IllegalStateException(e); + } finally { + close(conn, pstmt, rs); + } + } + + @Override + public List findAll() { + String sql = "select * from item"; + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + try { + conn = getConnection(); + pstmt = conn.prepareStatement(sql); + rs = pstmt.executeQuery(); + List items = new ArrayList<>(); + + while (rs.next()) { + Item item = new Item(); + item.setId(rs.getLong("id")); + item.setName(rs.getString("name")); + item.setCount(rs.getInt("count")); + item.setPrice(rs.getInt("price")); + items.add(item); + } + + return items; + } catch (Exception e) { + throw new IllegalStateException(e); + } finally { + close(conn, pstmt, rs); + } + } + + private Connection getConnection() { + return DataSourceUtils.getConnection(dataSource); + } + + private void close(Connection conn, PreparedStatement pstmt, ResultSet rs) { + try { + if (rs != null) { + rs.close(); + } + } + catch (SQLException e) { + e.printStackTrace(); + } + try { + if (pstmt != null) { + pstmt.close(); + } + } + catch (SQLException e) { + e.printStackTrace(); + } + try { + if (conn != null) { + close(conn); + } + } + catch (SQLException e) { + e.printStackTrace(); + } + } + + private void close(Connection conn) throws SQLException { + DataSourceUtils.releaseConnection(conn, dataSource); + } +} \ No newline at end of file diff --git a/src/main/java/landvibe/springintro/item/repository/JdbcTemplateItemRepository.java b/src/main/java/landvibe/springintro/item/repository/JdbcTemplateItemRepository.java new file mode 100644 index 0000000..1f78ac7 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/repository/JdbcTemplateItemRepository.java @@ -0,0 +1,64 @@ +package landvibe.springintro.item.repository; + +import landvibe.springintro.item.domain.Item; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class JdbcTemplateItemRepository implements ItemRepository { + private final JdbcTemplate jdbcTemplate; + + @Autowired + public JdbcTemplateItemRepository(DataSource dataSource) { + jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Override + public Item save(Item item) { + SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate); + jdbcInsert.withTableName("item").usingGeneratedKeyColumns("id"); + Map parameters = new HashMap<>(); + parameters.put("name", item.getName()); + parameters.put("count", item.getCount()); + parameters.put("price", item.getPrice()); + Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters)); + item.setId(key.longValue()); + return item; + } + + @Override + public Optional findById(Long id) { + List result = jdbcTemplate.query("select * from item where id = ? ", rowMapper(), id); + return result.stream().findAny(); + } + + @Override + public Optional findByName(String name) { + List result = jdbcTemplate.query("select * from item where name = ? ", rowMapper(), name); + return result.stream().findAny(); + } + + @Override + public List findAll() { + return jdbcTemplate.query("select * from item", rowMapper()); + } + + private RowMapper rowMapper() { + return (rs, rowNum) -> { + Item item = new Item(); + item.setId(rs.getLong("id")); + item.setName(rs.getString("name")); + item.setPrice(rs.getInt("price")); + item.setCount(rs.getInt("count")); + return item; + }; + } + +} diff --git a/src/main/java/landvibe/springintro/item/repository/JpaItemRepository.java b/src/main/java/landvibe/springintro/item/repository/JpaItemRepository.java new file mode 100644 index 0000000..6b78881 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/repository/JpaItemRepository.java @@ -0,0 +1,40 @@ +package landvibe.springintro.item.repository; + +import jakarta.persistence.EntityManager; +import landvibe.springintro.item.domain.Item; +import java.util.List; +import java.util.Optional; + +public class JpaItemRepository implements ItemRepository { + private final EntityManager em; + + public JpaItemRepository(EntityManager em) { + this.em = em; + } + + @Override + public Item save(Item item) { + em.persist(item); + return item; + } + + @Override + public Optional findById(Long id) { + Item item = em.find(Item.class, id); + return Optional.ofNullable(item); + } + + @Override + public Optional findByName(String name) { + return em.createQuery("select i from Item i where i.name = :name", Item.class) + .setParameter("name", name) + .getResultStream() + .findAny(); + } + + @Override + public List findAll() { + return em.createQuery("select i from Item i", Item.class) + .getResultList(); + } +} diff --git a/src/main/java/landvibe/springintro/item/repository/MemoryItemRepository.java b/src/main/java/landvibe/springintro/item/repository/MemoryItemRepository.java new file mode 100644 index 0000000..a6c4aa9 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/repository/MemoryItemRepository.java @@ -0,0 +1,40 @@ +package landvibe.springintro.item.repository; + +import landvibe.springintro.item.domain.Item; +import org.springframework.stereotype.Repository; + +import java.util.*; + +public class MemoryItemRepository implements ItemRepository { + private static Map store = new HashMap<>(); + private static long sequence = 0L; + + @Override + public Item save(Item item) { + item.setId(++sequence); + store.put(item.getId(), item); + return item; + } + + @Override + public Optional findById(Long id) { + Item item = store.get(id); + return Optional.ofNullable(item); + } + + @Override + public Optional findByName(String name) { + return store.values().stream() + .filter(item -> item.getName().equals(name)) + .findAny(); + } + + @Override + public List findAll() { + return new ArrayList<>(store.values()); + } + + public void clearStore() { + store.clear(); + } +} diff --git a/src/main/java/landvibe/springintro/item/repository/SpringDataJpaItemRepository.java b/src/main/java/landvibe/springintro/item/repository/SpringDataJpaItemRepository.java new file mode 100644 index 0000000..bf1ddf8 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/repository/SpringDataJpaItemRepository.java @@ -0,0 +1,9 @@ +package landvibe.springintro.item.repository; + +import landvibe.springintro.item.domain.Item; +import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; +public interface SpringDataJpaItemRepository extends JpaRepository, ItemRepository { + @Override + Optional findByName(String name); +} diff --git a/src/main/java/landvibe/springintro/item/service/ItemService.java b/src/main/java/landvibe/springintro/item/service/ItemService.java new file mode 100644 index 0000000..c2d6864 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/service/ItemService.java @@ -0,0 +1,43 @@ +package landvibe.springintro.item.service; + +import landvibe.springintro.item.domain.Item; +import landvibe.springintro.item.repository.ItemRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional +public class ItemService { + private final ItemRepository repository; + + public ItemService(ItemRepository repository) { + this.repository = repository; + } + + /** + * + 상품 등록 + */ + public Long create(Item item) { + validateDuplicateItem(item.getName()); + repository.save(item); + return item.getId(); + } + + /** + * + 상품 전체조회 + */ + public List findItems() { + return repository.findAll(); + } + + private void validateDuplicateItem(String itemName) { + repository.findByName(itemName) + .ifPresent(i -> { + throw new IllegalArgumentException("이미 존재하는 상품입니다."); + }); + } +} diff --git a/src/main/java/landvibe/springintro/member/config/SpringConfig.java b/src/main/java/landvibe/springintro/member/config/SpringConfig.java new file mode 100644 index 0000000..86999c5 --- /dev/null +++ b/src/main/java/landvibe/springintro/member/config/SpringConfig.java @@ -0,0 +1,56 @@ +package landvibe.springintro.member.config; + +import jakarta.persistence.EntityManager; +import landvibe.springintro.aop.TimeTraceAop; +import landvibe.springintro.member.repository.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Bean; +import landvibe.springintro.member.service.MemberService; + +import javax.sql.DataSource; + +@Configuration +public class SpringConfig { //둘 다 스프링 빈에 등록 + /* + private DataSource dataSource; + + @Autowired + public SpringConfig(DataSource dataSource) { + this.dataSource = dataSource; + } + */ + + //private EntityManager em; + + private final MemberRepository memberRepository; + + public SpringConfig(MemberRepository memberRepository) { + this.memberRepository = memberRepository; + } + + @Bean + public MemberService memberService() { + return new MemberService(memberRepository); + } + + /* + @Bean + public TimeTraceAop timeTraceAop() { + return new TimeTraceAop(); + } + */ + +// @Bean +// public MemberRepository memberRepository() { +// return new MemoryMemberRepository(); //나중에 레포지토리를 변경하면 다른거 일절 변경없이 얘만 수정해주면 끝 +// return new JdbcMemberRepository(dataSource); +// return new JdbcTemplateMemberRepository(dataSource); +// return new JpaMemberRepository(em); +// } +} + +/* +어떤 코드도 건들이지 않고 구현체를 확장. +스프링이 제공하는 config만 변경. + */ \ No newline at end of file diff --git a/src/main/java/landvibe/springintro/member/controller/MemberController.java b/src/main/java/landvibe/springintro/member/controller/MemberController.java new file mode 100644 index 0000000..77cfa97 --- /dev/null +++ b/src/main/java/landvibe/springintro/member/controller/MemberController.java @@ -0,0 +1,43 @@ +package landvibe.springintro.member.controller; + +import org.springframework.stereotype.Controller; +import landvibe.springintro.member.service.MemberService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import landvibe.springintro.member.domain.Member; + +import java.util.List; +@Controller //'스프링 컨테이너'라는 통이 생기는데, 거기에 MemberController 객체가 생성되고 스프링이 관리해 +public class MemberController { + private final MemberService memberService; //new MemberService() 라고 안하는 이유 : 여러 개를 생성할 필요가 없으니까. + + //생성자 주입 + @Autowired //스프링이 스프링 컨테이너에 있는 memberService를 가져다가 연결해줘 -> DI. 의존관계 주입 + public MemberController(MemberService memberService) { //객체 생성 + this.memberService = memberService; + } + + @GetMapping(value = "/members/new") //get은 조회할 때 주로 사용 + public String createForm() { + return "members/createMemberForm"; + } + + @PostMapping("/members/new") //post는 데이터를 폼같은 것에 넣어서 전달할 때 주로 사용 + public String create(MemberForm form) { + Member member = new Member(); + member.setName(form.getName()); + + memberService.join(member); //join 해서 멤버가 가입 + + return "redirect:/"; //회원가입 끝났으니까 다시 홈화면으로 돌려보내 + } + + @GetMapping(value = "/members") + public String list(Model model) { + List members = memberService.findMembers(); //멤버 전부를 끄집어올 수 있어 + model.addAttribute("members", members); + return "members/memberList"; + } +} diff --git a/src/main/java/landvibe/springintro/member/controller/MemberForm.java b/src/main/java/landvibe/springintro/member/controller/MemberForm.java new file mode 100644 index 0000000..e1ee1a0 --- /dev/null +++ b/src/main/java/landvibe/springintro/member/controller/MemberForm.java @@ -0,0 +1,13 @@ +package landvibe.springintro.member.controller; + +public class MemberForm { + private String name; //createMemberForm에 있는 name이랑 매칭될거야 + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/landvibe/springintro/member/domain/Member.java b/src/main/java/landvibe/springintro/member/domain/Member.java new file mode 100644 index 0000000..504090b --- /dev/null +++ b/src/main/java/landvibe/springintro/member/domain/Member.java @@ -0,0 +1,30 @@ +package landvibe.springintro.member.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity +public class Member { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) //db에 값을 넣으면 db가 id를 자동생성 -> identity + private Long id; + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/landvibe/springintro/member/repository/JdbcMemberRepository.java b/src/main/java/landvibe/springintro/member/repository/JdbcMemberRepository.java new file mode 100644 index 0000000..6d13d6f --- /dev/null +++ b/src/main/java/landvibe/springintro/member/repository/JdbcMemberRepository.java @@ -0,0 +1,172 @@ +package landvibe.springintro.member.repository; + +import landvibe.springintro.member.domain.Member; +import org.springframework.jdbc.datasource.DataSourceUtils; +import javax.sql.DataSource; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + + +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(name) values(?)"; + + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; //결과 받아 + + try { + conn = getConnection(); //connection 가져오고 + pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); //db에 insert 할 때 필요해 + + pstmt.setString(1, member.getName()); //?에 값을 넣어 + + pstmt.executeUpdate(); + rs = pstmt.getGeneratedKeys(); //인덱스를 꺼내줘 + + if (rs.next()) { + member.setId(rs.getLong(1)); + } + else { + throw new SQLException("id 조회 실패"); + } + + return member; + } catch (Exception e) { + throw new IllegalStateException(e); + } finally { + close(conn, pstmt, rs); + } + } + + @Override + public Optional findById(Long id) { + String sql = "select * from member where id = ?"; + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + + try { + conn = getConnection(); + pstmt = conn.prepareStatement(sql); + pstmt.setLong(1, id); + + rs = pstmt.executeQuery(); + + if(rs.next()) { + Member member = new Member(); + member.setId(rs.getLong("id")); + member.setName(rs.getString("name")); + return Optional.of(member); + } + else { + return Optional.empty(); + } + } catch (Exception e) { + throw new IllegalStateException(e); + } finally { + close(conn, pstmt, rs); + } + } + + @Override + public List findAll() { //전부 조회 + String sql = "select * from member"; + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + + try { + conn = getConnection(); + pstmt = conn.prepareStatement(sql); + rs = pstmt.executeQuery(); + List members = new ArrayList<>(); + + while (rs.next()) { + Member member = new Member(); + member.setId(rs.getLong("id")); + member.setName(rs.getString("name")); + members.add(member); + } + + return members; + } catch (Exception e) { + throw new IllegalStateException(e); + } finally { + close(conn, pstmt, rs); + } + } + + @Override + public Optional findByName(String name) { + String sql = "select * from member where name = ?"; + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; + + try { + conn = getConnection(); + pstmt = conn.prepareStatement(sql); + pstmt.setString(1, name); + rs = pstmt.executeQuery(); + + if(rs.next()) { + Member member = new Member(); + member.setId(rs.getLong("id")); + member.setName(rs.getString("name")); + return Optional.of(member); + } + + return Optional.empty(); + } catch (Exception e) { + throw new IllegalStateException(e); + } finally { + close(conn, pstmt, rs); + } + } + + private Connection getConnection() { + return DataSourceUtils.getConnection(dataSource); + } + + + private void close(Connection conn, PreparedStatement pstmt, ResultSet rs) { + try { + if (rs != null) { + rs.close(); + } + } + catch (SQLException e) { + e.printStackTrace(); + } + try { + if (pstmt != null) { + pstmt.close(); + } + } + catch (SQLException e) { + e.printStackTrace(); + } + try { + if (conn != null) { + close(conn); + } + } + catch (SQLException e) { + e.printStackTrace(); + } + } + + private void close(Connection conn) throws SQLException { + DataSourceUtils.releaseConnection(conn, dataSource); //자원 release + } +} \ No newline at end of file diff --git a/src/main/java/landvibe/springintro/member/repository/JdbcTemplateMemberRepository.java b/src/main/java/landvibe/springintro/member/repository/JdbcTemplateMemberRepository.java new file mode 100644 index 0000000..b6776f3 --- /dev/null +++ b/src/main/java/landvibe/springintro/member/repository/JdbcTemplateMemberRepository.java @@ -0,0 +1,62 @@ +package landvibe.springintro.member.repository; + +import landvibe.springintro.member.domain.Member; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.simple.SimpleJdbcInsert; +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class JdbcTemplateMemberRepository implements MemberRepository { + private final JdbcTemplate jdbcTemplate; + + @Autowired //생성자 하나이면 생략해도 가능 + public JdbcTemplateMemberRepository(DataSource dataSource) { + jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Override + public Member save(Member member) { + SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate); + jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id"); //테이블명이랑 열을 지정 -> 쿼리 짤 필요X + + Map parameters = new HashMap<>(); + parameters.put("name", member.getName()); + + Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(parameters)); + member.setId(key.longValue()); + return member; + } + + @Override + public Optional findById(Long id) { //jdbc와 비교해서 매우 짧아 + List result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(), id); + return result.stream().findAny(); + } + + @Override + public List findAll() { + return jdbcTemplate.query("select * from member", memberRowMapper()); + } + + @Override + public Optional findByName(String name) { + List result = jdbcTemplate.query("select * from member where name = ?", memberRowMapper(), name); + return result.stream().findAny(); + } + + private RowMapper memberRowMapper() { + return (rs, rowNum) -> { + Member member = new Member(); + member.setId(rs.getLong("id")); + member.setName(rs.getString("name")); + return member; + }; + } + +} diff --git a/src/main/java/landvibe/springintro/member/repository/JpaMemberRepository.java b/src/main/java/landvibe/springintro/member/repository/JpaMemberRepository.java new file mode 100644 index 0000000..62d88b5 --- /dev/null +++ b/src/main/java/landvibe/springintro/member/repository/JpaMemberRepository.java @@ -0,0 +1,42 @@ +package landvibe.springintro.member.repository; + +import landvibe.springintro.member.domain.Member; +import jakarta.persistence.EntityManager; +import java.util.List; +import java.util.Optional; + +public class JpaMemberRepository implements MemberRepository { + private final EntityManager em; //jpa 쓰려면 entityManager 주입 필요 + + public JpaMemberRepository(EntityManager em) { + this.em = em; + } + + @Override + public Member save(Member member) { + em.persist(member); + return member; + } + + @Override + public Optional findById(Long id) { + Member member = em.find(Member.class, id); + return Optional.ofNullable(member); + } + + @Override + public List findAll() { + return em.createQuery("select m from Member m", Member.class) + .getResultList(); + } + + @Override + public Optional findByName(String name) { + List result = em.createQuery("select m from Member m where m.name = :name", Member.class) + .setParameter("name", name) + .getResultList(); + + return result.stream().findAny(); + } + +} diff --git a/src/main/java/landvibe/springintro/member/repository/MemberRepository.java b/src/main/java/landvibe/springintro/member/repository/MemberRepository.java new file mode 100644 index 0000000..634179a --- /dev/null +++ b/src/main/java/landvibe/springintro/member/repository/MemberRepository.java @@ -0,0 +1,12 @@ +package landvibe.springintro.member.repository; + +import landvibe.springintro.member.domain.Member; +import java.util.List; +import java.util.Optional; + +public interface MemberRepository { + Member save(Member member); //저장소에 저장됨. + Optional findById(Long id); //없으면 null 반환. 요즘은 null 처리할 때 optional로 감싸서 반환. + Optional findByName(String name); + List findAll(); //저장된 모든 것을 반환. +} \ No newline at end of file diff --git a/src/main/java/landvibe/springintro/member/repository/MemoryMemberRepository.java b/src/main/java/landvibe/springintro/member/repository/MemoryMemberRepository.java new file mode 100644 index 0000000..771d110 --- /dev/null +++ b/src/main/java/landvibe/springintro/member/repository/MemoryMemberRepository.java @@ -0,0 +1,36 @@ +package landvibe.springintro.member.repository; + +import landvibe.springintro.member.domain.Member; +import landvibe.springintro.member.repository.MemberRepository; + +import java.util.*; + +//@Repository +public class MemoryMemberRepository implements MemberRepository { + private static Map store = new HashMap<>(); + private static long sequence = 0L; + + @Override + public Member save(Member member) { + member.setId(++sequence); + store.put(member.getId(), member); + return member; + } + @Override + public Optional findById(Long id) { + return Optional.ofNullable(store.get(id)); + } + @Override + public List findAll() { + return new ArrayList<>(store.values()); + } + @Override + public Optional findByName(String name) { + return store.values().stream() + .filter(member -> member.getName().equals(name)) + .findAny(); + } + public void clearStore() { + store.clear(); + } +} \ No newline at end of file diff --git a/src/main/java/landvibe/springintro/member/repository/SpringDataJpaMemberRepository.java b/src/main/java/landvibe/springintro/member/repository/SpringDataJpaMemberRepository.java new file mode 100644 index 0000000..c47deef --- /dev/null +++ b/src/main/java/landvibe/springintro/member/repository/SpringDataJpaMemberRepository.java @@ -0,0 +1,13 @@ +package landvibe.springintro.member.repository; + +import landvibe.springintro.member.domain.Member; +import landvibe.springintro.member.repository.MemberRepository; +import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + +public interface SpringDataJpaMemberRepository extends JpaRepository, MemberRepository { + @Override + Optional findByName(String name); +} + +//구현체를 알아서 만들어서 자동으로 스프링빈을 등록해 -> 우린 이걸 가져다 쓰기만 하면 돼 \ No newline at end of file diff --git a/src/main/java/landvibe/springintro/member/service/MemberService.java b/src/main/java/landvibe/springintro/member/service/MemberService.java new file mode 100644 index 0000000..11061eb --- /dev/null +++ b/src/main/java/landvibe/springintro/member/service/MemberService.java @@ -0,0 +1,52 @@ +package landvibe.springintro.member.service; + +import landvibe.springintro.member.domain.Member; +import landvibe.springintro.member.repository.MemberRepository; +import landvibe.springintro.member.repository.MemoryMemberRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Optional; + +//@Service +@Transactional +public class MemberService { //이에 해당하는 test 만들기 -> ctrl+shift+T 단축키 + private final MemberRepository memberRepository; + //MemberService를 스프링이 생성을 할 때 service네? 하고 생성자를 호출. + + //@Autowired //이게 autowired이면 memberRepository가 필요하구나. 하고 스프링 컨테이너에 있는 것을 넣어줘 + public MemberService(MemberRepository memberRepository) { //외부에서 memberRepository를 넣어줘 -> dependency injection + this.memberRepository = memberRepository; + } + + /** + * + 회원가입 + */ + public Long join(Member member) { + validateDuplicateMember(member); //중복 회원 검증 + memberRepository.save(member); + return member.getId(); //id 반환 + } + + private void validateDuplicateMember(Member member) { + memberRepository.findByName(member.getName()) + .ifPresent(m -> { //null이 아니고 어떤 값이 있으면 이 로직 동작 + throw new IllegalStateException("이미 존재하는 회원입니다."); + }); + } + + /** + * + 전체 회원 조회 + */ + public List findMembers() { + return memberRepository.findAll(); + } + + public Optional findOne(Long memberId) { + return memberRepository.findById(memberId); + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e7c1e8f..47d48fa 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,6 @@ spring.application.name=springintro +spring.datasource.url=jdbc:h2:tcp://localhost/~/test +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.username=sa +spring.jpa.show-sql=true +spring.jpa.hibernate.ddl-auto=none \ No newline at end of file diff --git a/src/main/resources/static/hello/static-file.html b/src/main/resources/static/hello/static-file.html index ff6263b..7dabd06 100644 --- a/src/main/resources/static/hello/static-file.html +++ b/src/main/resources/static/hello/static-file.html @@ -7,4 +7,4 @@ 땅울림 썸머코딩 2024 - \ No newline at end of file + diff --git a/src/main/resources/templates/fragments/header.html b/src/main/resources/templates/fragments/header.html index 33dff78..be4a2ec 100755 --- a/src/main/resources/templates/fragments/header.html +++ b/src/main/resources/templates/fragments/header.html @@ -10,6 +10,12 @@ integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> + + + + + + LANDVIBE diff --git a/src/test/java/landvibe/springintro/item/repository/MemoryItemRepositoryTest.java b/src/test/java/landvibe/springintro/item/repository/MemoryItemRepositoryTest.java new file mode 100644 index 0000000..fe91f17 --- /dev/null +++ b/src/test/java/landvibe/springintro/item/repository/MemoryItemRepositoryTest.java @@ -0,0 +1,66 @@ +package landvibe.springintro.item.repository; + +import landvibe.springintro.item.domain.Item; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import java.util.List; +import static org.assertj.core.api.Assertions.*; +class MemoryItemRepositoryTest { + MemoryItemRepository repository = new MemoryItemRepository(); + @AfterEach + void afterEach() { + repository.clearStore(); + } + + @Test + void save() { + // given + Item item = createItem("과자", 10, 1000); + + // when + repository.save(item); + + // then + Item foundItem = repository.findById(item.getId()).get(); + assertThat(foundItem).isEqualTo(item); + } + + @Test + void findByName() { + // given + Item 눈을감자 = createItem("눈을감자", 10, 1000); + Item 프링글스 = createItem("프링글스", 10, 1000); + repository.save(눈을감자); + repository.save(프링글스); + + // when + Item foundItem = repository.findByName("눈을감자").get(); + + // then + assertThat(foundItem).isEqualTo(눈을감자); + } + + @Test + void findAll() { + // given + Item 눈을감자 = createItem("눈을감자", 10, 1000); + Item 프링글스 = createItem("프링글스", 10, 1000); + repository.save(눈을감자); + repository.save(프링글스); + + // when + List foundItems = repository.findAll(); + + // then + assertThat(foundItems.size()).isEqualTo(2); + assertThat(foundItems).contains(눈을감자, 프링글스); + } + + private static Item createItem(String name, int count, int price) { + Item item = new Item(); + item.setName(name); + item.setCount(count); + item.setPrice(price); + return item; + } +} diff --git a/src/test/java/landvibe/springintro/item/service/ItemServiceIntegrationTest.java b/src/test/java/landvibe/springintro/item/service/ItemServiceIntegrationTest.java new file mode 100644 index 0000000..3e18d9f --- /dev/null +++ b/src/test/java/landvibe/springintro/item/service/ItemServiceIntegrationTest.java @@ -0,0 +1,59 @@ +package landvibe.springintro.item.service; + +import landvibe.springintro.item.domain.Item; +import landvibe.springintro.item.repository.ItemRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@SpringBootTest +//@Transactional +class ItemServiceIntegrationTest { + @Autowired + ItemService service; + + @Autowired + ItemRepository repository; + + @Test + public void 상품생성() throws Exception { + // given + Item item = createItem("눈을감자", 10, 1000); + + // when + Long id = service.create(item); + + // then + Item foundItem = repository.findById(id).get(); + assertThat(foundItem.getName()).isEqualTo(item.getName()); + assertThat(foundItem.getPrice()).isEqualTo(item.getPrice()); + assertThat(foundItem.getCount()).isEqualTo(item.getCount()); + } + + @Test + void 중복이름_상품예외() { + // given + Item item = createItem("눈을감자", 10, 1000); + Item duplicatedItem = createItem("눈을감자", 10, 1000); + + service.create(item); + + // when & then + IllegalArgumentException ex = + assertThrows(IllegalArgumentException.class, + () -> service.create(duplicatedItem));// 예외 발생 + assertThat(ex.getMessage()).isEqualTo("이미 존재하는 상품입니다."); + } + + private static Item createItem(String name, int count, int price) { + Item item = new Item(); + item.setName(name); + item.setCount(count); + item.setPrice(price); + + return item; + } +} diff --git a/src/test/java/landvibe/springintro/item/service/ItemServiceTest.java b/src/test/java/landvibe/springintro/item/service/ItemServiceTest.java new file mode 100644 index 0000000..9b6d7c3 --- /dev/null +++ b/src/test/java/landvibe/springintro/item/service/ItemServiceTest.java @@ -0,0 +1,62 @@ +package landvibe.springintro.item.service; + +import landvibe.springintro.item.domain.Item; +import landvibe.springintro.item.repository.ItemRepository; +import landvibe.springintro.item.repository.MemoryItemRepository; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.util.Optional; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +public class ItemServiceTest { + ItemService service; + MemoryItemRepository repository; + + @BeforeEach + void setUp() { + repository = new MemoryItemRepository(); + service = new ItemService(repository); + } + + @AfterEach + void tearDown() { + repository.clearStore(); + } + + @Test + public void 상품생성() throws Exception { + // given + Item item = createItem("눈을감자", 10, 1000); + + // when + Long id = service.create(item); + + // then + Item foundItem = repository.findById(id).get(); + assertThat(foundItem).isEqualTo(item); + } + + @Test + void 중복이름_상품예외() { + // given + Item item = createItem("눈을감자", 10, 1000); + Item duplicatedItem = createItem("눈을감자", 10, 1000); + service.create(item); + + // when & then + IllegalArgumentException ex = + assertThrows(IllegalArgumentException.class, + () -> service.create(duplicatedItem));// 예외 발생 + assertThat(ex.getMessage()).isEqualTo("이미 존재하는 상품입니다."); + } + + private static Item createItem(String name, int count, int price) { + Item item = new Item(); + item.setName(name); + item.setCount(count); + item.setPrice(price); + return item; + } +} diff --git a/src/test/java/landvibe/springintro/member/repository/MemoryMemberRepositoryTest.java b/src/test/java/landvibe/springintro/member/repository/MemoryMemberRepositoryTest.java new file mode 100644 index 0000000..ed610dc --- /dev/null +++ b/src/test/java/landvibe/springintro/member/repository/MemoryMemberRepositoryTest.java @@ -0,0 +1,68 @@ +package landvibe.springintro.member.repository; + +import landvibe.springintro.member.domain.Member; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.*; +import java.util.List; +import org.junit.jupiter.api.AfterEach; + +class MemoryMemberRepositoryTest { + MemoryMemberRepository repository = new MemoryMemberRepository(); + + @AfterEach //test들은 서로 의존관계 없어야해.!! + public void afterEach() { //test가 실행되고 끝날때마다 한번씩 저장소를 지워. -> 순서상관없어져. + repository.clearStore(); + } + + @Test + public void save() { + Member member = new Member(); + member.setName("spring"); + + repository.save(member); + + //내가 넣은게 제대로 들어갔는지 검증을 해. 반환타입이 optional. 여기서 꺼낼 때는 get을 사용. + Member result = repository.findById(member.getId()).get(); + //Assertions.assertEquals(member, result); member==result 인지 확인. + assertThat(member).isEqualTo(result); //역시 result 자리에 null을 넣으면 에러가 나. + } + + @Test + public void findByName() { + //given + Member member1 = new Member(); + member1.setName("spring1"); + repository.save(member1); + + Member member2 = new Member(); //정교한 test를 위해 객체 하나 더 생성. -> shift+F6 누르면 한번에 수정 가능. + member2.setName("spring2"); + repository.save(member2); + //when + Member result = repository.findByName("spring1").get(); //spring1을 찾아보는거야. get으로 꺼내봐. + //then + assertThat(result).isEqualTo(member1); + } + + @Test + public void findAll() { + //given + Member member1 = new Member(); + member1.setName("spring1"); + repository.save(member1); + Member member2 = new Member(); + member2.setName("spring2"); + repository.save(member2); + //when + List result = repository.findAll(); + //then + assertThat(result.size()).isEqualTo(2); + } +} + +/* +findByName에서 에러가 나. 왜일까? +모든 test의 순서는 보장X. findAll이 먼저 실행됨. spring1과 spring2가 이미 저장된거야. +그래서 findByName에서 이전에 저장된게 나와버려. + +따라서..!! test가 하나 끝나고나면 data를 clear 해줘야해. +*/ \ No newline at end of file diff --git a/src/test/java/landvibe/springintro/member/service/MemberServiceIntegrationTest.java b/src/test/java/landvibe/springintro/member/service/MemberServiceIntegrationTest.java new file mode 100644 index 0000000..dfd3ace --- /dev/null +++ b/src/test/java/landvibe/springintro/member/service/MemberServiceIntegrationTest.java @@ -0,0 +1,49 @@ +package landvibe.springintro.member.service; + +import org.junit.jupiter.api.Test; +import landvibe.springintro.member.domain.Member; +import landvibe.springintro.member.repository.MemberRepository; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.Commit; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest //스프링 컨테이너와 테스트를 함께 실행 +@Transactional //test 실행할 때 transaction을 먼저 실행. test 후 roll back. -> 넣었던 데이터 다 없어져 +class MemberServiceIntegrationTest { + @Autowired MemberService memberService; + @Autowired MemberRepository memberRepository; + + @Test + public void 회원가입() throws Exception { + //Given : 무언가가 주어졌는데 + Member member = new Member(); + member.setName("spring"); + + //When : 이걸 실행했을 때 + Long saveId = memberService.join(member); //저장한 id가 리턴되기로 해놨으니까 + + //Then : 결과가 이게 나와야해 + Member findMember = memberRepository.findById(saveId).get(); + assertEquals(member.getName(), findMember.getName()); + } + + @Test + public void 중복_회원_예외() throws Exception { + //Given + Member member1 = new Member(); + member1.setName("spring"); + + Member member2 = new Member(); + member2.setName("spring"); //멤버2도 spring이라 해 + + //When + memberService.join(member1); //문제가 없어야하고 + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> memberService.join(member2));//예외가 발생해야해 + assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다."); + } +} + diff --git a/src/test/java/landvibe/springintro/member/service/MemberServiceTest.java b/src/test/java/landvibe/springintro/member/service/MemberServiceTest.java new file mode 100644 index 0000000..f8bc955 --- /dev/null +++ b/src/test/java/landvibe/springintro/member/service/MemberServiceTest.java @@ -0,0 +1,54 @@ +package landvibe.springintro.member.service; + +import org.junit.jupiter.api.Test; +import landvibe.springintro.member.domain.Member; +import landvibe.springintro.member.repository.MemoryMemberRepository; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.AfterEach; + +class MemberServiceTest { + MemberService memberService; + MemoryMemberRepository memberRepository; + + @BeforeEach + public void beforeEach() { + memberRepository = new MemoryMemberRepository(); + memberService = new MemberService(memberRepository); + } + + @AfterEach + public void afterEach() { + memberRepository.clearStore(); + } + + @Test + public void 회원가입() throws Exception { + //Given : 무언가가 주어졌는데 + Member member = new Member(); + member.setName("hello"); + + //When : 이걸 실행했을 때 + Long saveId = memberService.join(member); //저장한 id가 리턴되기로 해놨으니까 + + //Then : 결과가 이게 나와야해 + Member findMember = memberRepository.findById(saveId).get(); // + assertEquals(member.getName(), findMember.getName()); + } + + @Test + public void 중복_회원_예외() throws Exception { + //Given + Member member1 = new Member(); + member1.setName("spring"); + Member member2 = new Member(); + member2.setName("spring"); //멤버2도 spring이라 해 + + //When + memberService.join(member1); //문제가 없어야하고 + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> memberService.join(member2));//예외가 발생해야해 + assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다."); + } +} \ No newline at end of file