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..b6bd7c0 --- /dev/null +++ b/sql/ddl.sql @@ -0,0 +1,16 @@ +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) +); +drop table if exists member CASCADE; + create table member + ( + id bigint generated by default as identity, + name varchar(255), + primary key (id) + ); 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..a8f063f --- /dev/null +++ b/src/main/java/landvibe/springintro/Controller/HelloController.java @@ -0,0 +1,41 @@ +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","hello!"); + return "hello"; + } + @GetMapping("hello-mvc") + public String helloMvc(@RequestParam(value = "name") 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..3682983 --- /dev/null +++ b/src/main/java/landvibe/springintro/Controller/HomeController.java @@ -0,0 +1,10 @@ +package landvibe.springintro.Controller; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +@Controller +public class HomeController { + @GetMapping("/") + public String home() { + return "home"; + } +} \ 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..ca43b40 --- /dev/null +++ b/src/main/java/landvibe/springintro/aop/TimeTraceAop.java @@ -0,0 +1,21 @@ +package landvibe.springintro.aop; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.stereotype.Component; +@Component +@Aspect +public class TimeTraceAop { + @Around("execution(* hello.hellospring..*(..))") + 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/item/config/ItemConfig.java b/src/main/java/landvibe/springintro/item/config/ItemConfig.java new file mode 100644 index 0000000..b6a152a --- /dev/null +++ b/src/main/java/landvibe/springintro/item/config/ItemConfig.java @@ -0,0 +1,44 @@ +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); + } + */ +} \ No newline at end of file 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..468ad1f --- /dev/null +++ b/src/main/java/landvibe/springintro/item/controller/ItemController.java @@ -0,0 +1,40 @@ +package landvibe.springintro.item.controller; + +import landvibe.springintro.item.domain.Item; +import landvibe.springintro.item.service.ItemService; +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.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; + +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..e13ef72 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/controller/ItemCreateForm.java @@ -0,0 +1,24 @@ +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; + } +} \ No newline at end of file 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..8ad8fe1 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/domain/Item.java @@ -0,0 +1,42 @@ +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; + } +} \ No newline at end of file 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..26f9f96 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/repository/ItemRepository.java @@ -0,0 +1,11 @@ +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(); +} \ No newline at end of file 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..b89477d --- /dev/null +++ b/src/main/java/landvibe/springintro/item/repository/JdbcItemRepository.java @@ -0,0 +1,148 @@ +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..f463ec4 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/repository/JdbcTemplateItemRepository.java @@ -0,0 +1,54 @@ +package landvibe.springintro.item.repository; +import landvibe.springintro.item.domain.Item; +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; + 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..735bb48 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/repository/JpaItemRepository.java @@ -0,0 +1,39 @@ +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(); + } +} \ No newline at end of file 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..8c6edc6 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/repository/MemoryItemRepository.java @@ -0,0 +1,34 @@ +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(); + } +} \ No newline at end of file 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..d4336b6 --- /dev/null +++ b/src/main/java/landvibe/springintro/item/repository/SpringDataJpaItemRepository.java @@ -0,0 +1,8 @@ +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..db1d71f --- /dev/null +++ b/src/main/java/landvibe/springintro/item/service/ItemService.java @@ -0,0 +1,34 @@ +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.springframework.transaction.annotation.Transactional; + +import java.util.List; +@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("이미 존재하는 상품입니다."); + }); + } +} \ No newline at end of file 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..66e74f5 --- /dev/null +++ b/src/main/java/landvibe/springintro/member/config/SpringConfig.java @@ -0,0 +1,30 @@ +package landvibe.springintro.member.config; + +import landvibe.springintro.member.repository.*; +import landvibe.springintro.member.service.MemberService; +import jakarta.persistence.EntityManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import javax.sql.DataSource; + +@Configuration +public class SpringConfig { + + private final MemberRepository memberRepository; + public SpringConfig(MemberRepository memberRepository) { + this.memberRepository = memberRepository; + } + @Bean //스프링 빈을 등록할거야 //이게 싱글톤 ..??이라고하셧나그럼이밑에잇는코드가 한객체인가 + public MemberService memberService() { + return new MemberService(memberRepository); + } + // @Bean + //public MemberRepository memberRepository() { + //return new MemoryMemberRepository(); + //return new JdbcMemberRepository(dataSource); + //return new JdbcTemplateMemberRepository(dataSource); + //return new JpaMemberRepository(em); + + //} +} \ 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..2a27a38 --- /dev/null +++ b/src/main/java/landvibe/springintro/member/controller/MemberController.java @@ -0,0 +1,39 @@ +package landvibe.springintro.member.controller; + +import landvibe.springintro.member.domain.Member; +import landvibe.springintro.member.service.MemberService; +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 java.util.List; + +@Controller +public class MemberController { + private MemberService memberService; + @Autowired + //@autowired private final~~ <-필드 주입방식 + public MemberController(MemberService memberService) { //생성자 주입 방식 + this.memberService = memberService; + } + @GetMapping(value = "/members/new") //url창에 딱 치는건 Get맵핑 + public String createForm() { + return "members/createMemberForm"; + } + @PostMapping(value = "/members/new") //포스트 맵핑은 데이터를 폼깥은데 넣어서 전달할 때 씀 + //데이터 등록할때는 보통 post를 씀 + public String create(MemberForm form) { + Member member = new Member(); + member.setName(form.getName()); + memberService.join(member); + return "redirect:/"; //회원가입 끝났으니까 홈화면으로 돌림 + } + @GetMapping(value = "/members") + public String list(Model model) { + List members = memberService.findMembers(); + model.addAttribute("members", members); + return "members/memberList"; + } +} \ No newline at end of file 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..7fd1eee --- /dev/null +++ b/src/main/java/landvibe/springintro/member/controller/MemberForm.java @@ -0,0 +1,11 @@ +package landvibe.springintro.member.controller; + +public class MemberForm { + private String 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..0ca8655 --- /dev/null +++ b/src/main/java/landvibe/springintro/member/domain/Member.java @@ -0,0 +1,29 @@ +package landvibe.springintro.member.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +//jpa를 쓰려면 먼저 엔티티라는 것을 맵핑해야함. jpa는 인터페이스 +//JPA는 자바 진영의 표준 인터페이스고 구현은 여러 업체들이 하는거임 +//JPA는 객체랑 ORM(object,relational(관계형디비),mapping)기술 +@Entity +public class Member { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) //이 정보를 가지고 insert문 + //update문 등 다 만들 수 있음. + 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; + } +} \ No newline at end of file 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..96bd362 --- /dev/null +++ b/src/main/java/landvibe/springintro/member/repository/JdbcMemberRepository.java @@ -0,0 +1,148 @@ +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(?)"; //save할 sql + Connection conn = null; + PreparedStatement pstmt = null; + ResultSet rs = null; //ResultSet: 결과를 받는거임. + try { + conn = getConnection(); + pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + 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); //sql날리고 + pstmt.setLong(1, id); //prepare statement세팅 + + rs = pstmt.executeQuery();//조회는 execute update가 아님 + 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); + } +} \ 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..fbf1f1d --- /dev/null +++ b/src/main/java/landvibe/springintro/member/repository/JdbcTemplateMemberRepository.java @@ -0,0 +1,57 @@ +package landvibe.springintro.member.repository; + +import landvibe.springintro.member.domain.Member; +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.sql.ResultSet; +import java.sql.SQLException; +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; + public JdbcTemplateMemberRepository(DataSource dataSource) { + jdbcTemplate = new JdbcTemplate(dataSource); + } + @Override + public Member save(Member member) { + SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate); + jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id"); + 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) { + List result = jdbcTemplate.query("select * from member where id = ?", memberRowMapper(), id); + //결과를 RowMapper로 매핑해줘야함. + 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; + }; + } +} \ No newline at end of file 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..5daf39c --- /dev/null +++ b/src/main/java/landvibe/springintro/member/repository/JpaMemberRepository.java @@ -0,0 +1,32 @@ +package landvibe.springintro.member.repository; + +import landvibe.springintro.member.domain.Member; +import jakarta.persistence.EntityManager; + +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; + } + public Member save(Member member) { + em.persist(member); //영구저장하다 + return member; + }//이렇게하면 jpa가 인서트 쿼리 다 만들어서 db에 집어넣고 id까지 setid해줌 + public Optional findById(Long id) { + Member member = em.find(Member.class, id); + return Optional.ofNullable(member); + } + public List findAll() { + return em.createQuery("select m from Member m", Member.class) + .getResultList(); + } //엔티티를 대상으로 쿼리를 날림 객체 자체를 select함 + 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(); + } +} \ No newline at end of file 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..1395cce --- /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); //id로 회원찾기 + Optional findByName(String name); //optional은 findByname이 없을수도 있잖아. 없으면 null로 반환되겠지 + //null로 반환되는 대신 Optional로 감싸서 반환 + 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..d86f9ac --- /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 org.springframework.stereotype.Repository; + +import java.util.*; +/** + * 동시성 문제가 고려되어 있지 않음, 실무에서는 ConcurrentHashMap, AtomicLong 사용 고려 + */ + +public class MemoryMemberRepository implements MemberRepository { + private static Map store = new HashMap<>(); //key는 id 값은 member + private static long sequence = 0L; //012 처럼 key값 생성해줌 + @Override + public Member save(Member member) { + member.setId(++sequence); //ID는 시스템이 정해줌 + store.put(member.getId(), member); //맵에 저장됨 + return member; + } + @Override + public Optional findById(Long id) { + return Optional.ofNullable(store.get(id)); //결과가 없으면 null이 반환되지않도록 optional로 감싸줌. + } + @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)) //member.getName이 파라미터로 넘어온 name이랑 같은지 + .findAny(); //맵에서 돌면서 하나 찾으면 걔를 그냥 반환. 끝까지 돌렸는데 없으면 optional null반환 + } + public void clearStore() { + store.clear(); + } +} 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..c2d1425 --- /dev/null +++ b/src/main/java/landvibe/springintro/member/repository/SpringDataJpaMemberRepository.java @@ -0,0 +1,10 @@ +package landvibe.springintro.member.repository; + +import landvibe.springintro.member.domain.Member; +import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; +public interface SpringDataJpaMemberRepository extends JpaRepository, MemberRepository { + Optional findByName(String name); +} +//JPA가 JPA리포지토리를 받고있으면 얘가 구현체를 자동으로 만들어줌. +//내가 스프링 빈에 등록하는게 아니라 JPA가 지가 구현체 만들어서 등록해줌 \ 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..ec4bedc --- /dev/null +++ b/src/main/java/landvibe/springintro/member/service/MemberService.java @@ -0,0 +1,58 @@ +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 java.util.List; +import java.util.Optional; +import org.springframework.transaction.annotation.Transactional; +@Transactional +public class MemberService { + private final MemberRepository memberRepository; + + + public MemberService(MemberRepository memberRepository) { + this.memberRepository = memberRepository; + } + /** + *회원가입 + */ + public Long join(Member member) { + //같은 이름이 있는 중복 회원X + long start = System.currentTimeMillis(); + try { + validateDuplicateMember(member); //중복 회원 검증 메서드 + memberRepository.save(member); + return member.getId(); //id반환 + }finally{ + long finish = System.currentTimeMillis(); + long timeMs = finish - start; + System.out.println("join " + timeMs + "ms"); + } + } + private void validateDuplicateMember(Member member) { + memberRepository.findByName(member.getName()) + .ifPresent(m -> { + throw new IllegalStateException("이미 존재하는 회원입니다."); + }); + }//중복회원잡는게 잘 작동하는지 보기위해 테스트케이스 활용 + /** + *전체 회원 조회 + */ + public List findMembers() { + long start = System.currentTimeMillis(); + try{ + return memberRepository.findAll(); + }finally { + long finish = System.currentTimeMillis(); + long timeMs = finish - start; + System.out.println("findMembers " + timeMs + "ms"); + } + } + public Optional findOne(Long memberId) { + return memberRepository.findById(memberId); + } +} 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/templates/fragments/header.html b/src/main/resources/templates/fragments/header.html index 33dff78..0308fe9 100755 --- a/src/main/resources/templates/fragments/header.html +++ b/src/main/resources/templates/fragments/header.html @@ -10,6 +10,11 @@ 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..a76bb24 --- /dev/null +++ b/src/test/java/landvibe/springintro/item/repository/MemoryItemRepositoryTest.java @@ -0,0 +1,55 @@ +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; + } +} \ No newline at end of file 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..01f2415 --- /dev/null +++ b/src/test/java/landvibe/springintro/item/service/ItemServiceIntegrationTest.java @@ -0,0 +1,48 @@ +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; + } +} \ No newline at end of file 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..2ce26b0 --- /dev/null +++ b/src/test/java/landvibe/springintro/item/service/ItemServiceTest.java @@ -0,0 +1,53 @@ +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.*; +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; + } +} \ No newline at end of file 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..e4a1f8d --- /dev/null +++ b/src/test/java/landvibe/springintro/member/repository/MemoryMemberRepositoryTest.java @@ -0,0 +1,54 @@ +package landvibe.springintro.member.repository; + +import landvibe.springintro.member.domain.Member; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import java.util.List; +import java.util.Optional; +import static org.assertj.core.api.Assertions.*; +class MemoryMemberRepositoryTest { + MemoryMemberRepository repository = new MemoryMemberRepository(); + @AfterEach + public void afterEach() { + repository.clearStore(); //테스트가 실행되고 끝날때마다 한번씩 저장소를 다 치움->순서와 상관이 없어짐 + } + @Test + public void save() { + //given + Member member = new Member(); + member.setName("spring"); + //when + repository.save(member);//리포지토리에 세이브 + //then + Member result = repository.findById(member.getId()).get();//옵셔널에서 값을 꺼낼때는 .get()으로 꺼내기 + assertThat(result).isEqualTo(member); //같은지확인 + } + @Test + public void findByName() { + //given + Member member1 = new Member(); + member1.setName("spring1"); + repository.save(member1); + Member member2 = new Member(); + member2.setName("spring2"); + repository.save(member2); + //when + Member result = repository.findByName("spring1").get(); + //then + assertThat(result).isEqualTo(member1); //result는 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); //사이즈는 2개가 나와야한당 + } +} 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..3019777 --- /dev/null +++ b/src/test/java/landvibe/springintro/member/service/MemberServiceIntegrationTest.java @@ -0,0 +1,43 @@ +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 //디비에 넣었던 데이터가 반영안되고 지워짐 +class MemberServiceIntegrationTest { + @Autowired MemberService memberService; + @Autowired MemberRepository memberRepository; //테스트이기때문에 편한 auto쓰기 + @Test + + public void 회원가입() throws Exception { + //Given + Member member = new Member(); + member.setName("spring100000"); + //When + Long saveId = memberService.join(member); + //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"); + //When + memberService.join(member1); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> memberService.join(member2));//예외가 발생해야 한다. + assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다."); + } +} \ No newline at end of file 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..42feb91 --- /dev/null +++ b/src/test/java/landvibe/springintro/member/service/MemberServiceTest.java @@ -0,0 +1,51 @@ +package landvibe.springintro.member.service; +import landvibe.springintro.member.domain.Member; +import landvibe.springintro.member.repository.MemoryMemberRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +class MemberServiceTest { + MemberService memberService; + MemoryMemberRepository memberRepository; + + @BeforeEach + public void beforeEach() { //테스트 실행할때마다 각각 생성 + memberRepository = new MemoryMemberRepository(); + memberService = new MemberService(memberRepository); //같은 메모리 멤버 리포지토리가 사용. + //멤버 서비스 입장에서 얘가 직접 new하지 않지 외부에서 넣어주지. 이런걸 Dependency injection + } + + @AfterEach + public void afterEach() { + memberRepository.clearStore(); + } + + @Test + //Given 뭔가가 주어졌는데 when 이거를 실행했을때 then 결과가 이게 나와야돼 + public void 회원가입() throws Exception { + //Given + Member member = new Member(); + member.setName("hello"); + //When + Long saveId = memberService.join(member); //join검증 + //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"); + //When + memberService.join(member1); + IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));//예외가 발생해야 한다. + assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다."); + } +}