java 스프링부트 ( spring boot ) 시작하기 ( 2 )

김동명·2022년 12월 11일
0

스프링부트

목록 보기
3/19

이론

  • 의존성 주입 ( DI : Dependency Injection )
    • 객체간의 의존성이 존재 할 경우 개발자가 직접 객체를 생성하거나 제어하는 것이 아니라, 제어 반전에 의하여 특정 객체에 필요한 다른 객체를 프레임 워크가 자동으로 연결시켜주는 것을 말한다.
    • 개발자는 자신에게 필요한 객체를 직접 할당하지 않고, 인터페이스를 통해 선언한 객체에 스프링 프레임워크에 의해 주입받아 사용 할 수 있기 때문에 비즈니스 로직에만 집중 할 수 있다.
    • 개발자는 객체를 선언 할 뿐, 할당은 프레임워크에서 자동으로 이루어진다.

  • 제어역전 ( IoC, Inversion of Control )

    • 개발자가 프레임워크의 기능을 호출하는 형태가 아니라 프레임워크가 개발자의 코드를 호출하기 때문에, 개발자는 전체를 직접 구현하지 않고 자신의 코드를 부분적으로 "끼워넣기" 하는 형태로 구현 할 수 있다.
    • 프레임워크가 객체의 생성, 소멸과 같은 라이프 사이클을 관리하며 스프링으로부터 필요한 객체를 얻어 올 수 있다.
    • 객체의 의존성을 역전시켜 객체 간의 결합도를 줄이고 유연한 코드를 작성 할 수 있게하여 가독성 및 코드 중복, 유지보수를 편하게 할 수 있게 한다.

  • 스프링 컨테이너란?

    • 스프링은 실행시 객체들을 담고 있는 컨테이너가 있다.
    • 스프링 컨테이너는 자바 객체의 생명 주기를 관리하며, 생성된 자바 객체들에게 추가적인 기능을 제공하는 역할을 한다.
    • 여기서 말하는 자바 객체를 스프링에서는 빈 ( Bean ) 이라고 부른다.
    • 개발자는 객체를 생성하고 소멸 할 수 있는데, 스프링 컨테이너가 이 역할을 대신해 준다. 즉, 제어의 흐름을 외부에서 관리하는 것. 또한, 객체들 간의 의존관계를 스프링 컨테이너가 런타임 과정에서 알아서 만들어 준다.
    • 스프링은 스프링 컨테이너에 스프링 Bean ( 객체 ) 를 등록 할 때, 기본으로 싱글톤으로 등록한다.

  • POJO ( Plain Old Java Object ) 란?

    • POJO란, 객체 지향적인 원리에 충실하면서 환경과 기술에 종속되지 않고 필요에 따라 재활용 될 수 있는 방식으로 설계된 오브젝트를 말한다.
    • POJO에 애플리케이션의 핵심 로직과 기능을 담아 설계하고 개발하는 방법을 POJO프로그래밍이라고 할 수 있다.


프로젝스 세팅

  • 프로젝트 생성

  • help > Install new software

    • 설치

  • application.properties
#port
server.port=9090

#thymeleaf cache
spring.thymeleaf.cache=false


시작

1. 메모리를 이용한 방법

1. 기본 세팅하기

controller, Service, Repository 이해


- core2.comtroller , core2.member 패키지 생성
  • core2.member 패키지 > Member.java 생성
  • getter, setter 생성
public class Member {
	
	private int id;
	private String name;
	
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

  • core2.repository 패키지 생성

  • core2.repository > MemberRepository.java 인터페이스 생성

public interface MemberRepository {

	// 회원 저장
	Member save(Member member);
	
	// 전체 찾기
	List<Member> findAll();
	
}
  • repository > MemoryMemberRepository.java 생성
    • Map : 메모리 사용 ( 저장공간 만들기 ) 현재 디비가 없는 상태이기때문에 메모리 영역에 저장
    • sequence : 자동증가 시킬 시퀀스값
public class MemoryMemberRepository implements MemberRepository{

	private static Map<Integer, Member> store = new HashMap<Integer, Member>();
	private static int sequence = 0; 
    
	@Override
	public Member save(Member member) {
		member.setId(++sequence);
		store.put(member.getId(), member);
		return member;
	}

	@Override
	public List<Member> findAll() {
		return new ArrayList<Member>(store.values());  // 스토어 안에있는 전체 벨류 리턴
	}
  • core2.service 패키지 생성

  • core2.service > MemberService.java 생성

public class MemberService {
	
	MemberRepository memberRepository = new MemoryMemberRepository();
    
    // 회원가입
	public int join(Member member) {
		memberRepository.save(member);
		return member.getId();
	}
    
    // 전체회원 조회
	public List<Member> findMembers(){
		return memberRepository.findAll();
	}
	
}    
  • core2.controller > MemberController.java 생성
    • Controller가 Service를 의존
    • Service도 repository를 의존하고있음
    • Service는 여러 Controller에서 가져다 쓸 수 있기 때문에 Spring Controller에 등록을 해야 한다.
@Controller
public class MemberController {
	
    MemberService mServive = new MemberService();
}
  • MemberController.java 수정
    • 스프링스럽게 작업하기
    • Service는 Spring Container에 하나만 생성 및 등록해서 같이 공유해서 쓸 수 있다.
    • @Autowired : MemberController가 생성되면 스프링이 memberService와 연결을 해준다.
    • 서버 기동시에 에러 발생시 바로 표현되는 장점이 있다.
    • 생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다. 이렇게 의존관계를 외부에서 넣어주는 것을 DI ( Dependency Injection ) , 의존성 주입이라고 한다.
...
//	MemberService mServive = new MemberService();
	
	private final MemberService memberServive;
	
	@Autowired
	public MemberController(MemberService memberService) {
		this.memberServive = memberService;
	}
}

  • 의존성 주입의 다른 방법
//	-------------------------------------------------
	// 필드 주입 ( Field Injection )
	// - final 키워드를 사용 할 수 없어, 순환 참조가 발생 할 수 있다. 권장하지 않는다.
	@Autowired
	private MemberService memberServive;
//	-------------------------------------------------
	
//	-------------------------------------------------
	// Setter Injection ( 수정자 주입 ) 
	// - public으로 노출이 되기 때문에 다른곳에서 주입 가능하다.
	private MemberService memberService;
	
	@Autowired
	public void setMember(MemberService memberService) {
		this.memberService = memberService;
	}
//	-------------------------------------------------

  • MemberService.java 수정
    • @Service 추가
    • @Autowired : 스프링 컨테이너에 등록
@Service
public class MemberService {
 	//	MemberRepository memberRepository = new MemoryMemberRepository();
	private final MemberRepository memberRepository;
	
	@Autowired
	public MemberService(MemberRepository memberRepository) {
		this.memberRepository = memberRepository;
	}
}
  • MemoryMemberRepository.java 수정
    • @Repository 추가
@Repository
public class MemoryMemberRepository implements MemberRepository{
...
  

2. 기본 페이지 설정

Controller 패키지 > HomeController.java 생성

  • HomeController.java 생성
    • 기본 localhost:9090 으로 들어오면 여기를 호출
@Controller
public class HomeController {
	
	@GetMapping("/")
	public String home() {
		return "home";
	}
}

resoureces>templates> home.html 생성

  • home.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<div>
		<h1>Hello Spring</h1>
		<p>회원 기능</p>
		<p>
			<a href="/members/new">회원가입</a>
			<a href="/members">회원목록</a>
		</p>
	</div>
</body>
</html>

  • 출력

3. 회원가입 페이지 만들기

  • MemberController.java 수정
...
	@GetMapping(value = "/members/new")
	public String createForm() {
	return "members/createMemberForm";		
	}
}
  • templates.members 폴더 생성 > CreateMemberForm.html 생성
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="/members/new" method="post">
		<label for="name">이름</label>
		<input type="text" id="name" name="name" placeholder="이름을 입력하세요.">
		<button type="submit">등록</button>
	</form>
</body>
</html>
  • 출력
  • core2.member 패키지 > MemberFormDTO.java 생성
public class MemberFormDTO {

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
  • MemberController.java 수정
	@PostMapping(value = "/members/new")
	public String create(MemberFormDTO form) {
		Member member = new Member();
		member.setName(form.getName());
		
		memberServive.join(member);
		
		//리다이렉트 방식으로 홈 화면으로 돌리기. 
		return "redirect:/"; 
		//return "forward:/:;
	}

4. 회원목록 페이지 만들기

  • MemberController.java 수정
...
	@GetMapping("/members")
	public String list(Model model) {
		List<Member> members = memberServive.findMembers();
		model.addAttribute("members", members);
		return "members/memberList";
	}
}
    • templates.members 폴더 생성 > memberList.html 생성
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<div>
		<table border="1">
			<thead>
				<tr>
					<th>#</th>
					<th>이름</th>
				</tr>
			</thead>
			<tbody>
				<tr th:each="member : ${members}" >
					<td th:text="${member.id}"></td>
					<td th:text="${member.name}"></td>
				</tr>
			</tbody>			
		</table>
	</div>
</body>
</html>
  • 출력

2. DB를 이용한 방법 - JDBC

1. DB 계정 설정

2. 테이블 생성

3. build.gradle 수정

  • dependencies 수정
  • 수정후 build.gradle 우클릭 > gradle > Refresh gradle project
...

	implementation 'org.springframework.boot:spring-boot-starter-jdbc'
	implementation group: 'com.oracle.database.jdbc', name: 'ojdbc6', version: '11.2.0.4'

...

4. application.properties 수정

  • 추가
...
#encoding
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.force=true
server.servlet.encoding.enabled=true

#dbms
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:XE
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.username=myspring
spring.datasource.password=myspring

5. JdbcMemberRepository 생성

  • MemoryMemberRepository 수정
    • @Repository 주석처리
//@Repository
public class MemoryMemberRepository implements MemberRepository{
...
  • repository 패키지 > JdbcMemberRespository.java 생성
    • @Repository 변경을 통해 간단하게 Repository 변경
@Repository
public class JdbcMemberRepository implements MemberRepository{

	private final DataSource dataSource;
    
    public JdbcMemberRepository(DataSource dataSource) {
	this.dataSource = dataSource;
	}
    
    @Override
	public Member save(Member member) {
		String sql = "INSERT INTO MEMBER VALUES (member_seq.nextval, ?)";
		
		Connection conn = null;
		PreparedStatement pstm = null;
		ResultSet rs = null;
		
		try {
			conn = dataSource.getConnection();
			String generatedColumes[] = {"id"};
			pstm = conn.prepareStatement(sql, generatedColumes);
			pstm.setString(1, member.getName());
			pstm.executeUpdate();
			rs = pstm.getGeneratedKeys();
			
			if (rs.next()) {
				member.setId(rs.getInt(1));
			}
			
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			try {
				
			rs.close();
			pstm.close();
			conn.close();
			} catch( SQLException e) {
				e.printStackTrace();
			}
		}
		
		return member;
	}
    
    @Override
	public List<Member> findAll() {
		
		String sql = "select * from member";
		
		Connection conn = null;
		PreparedStatement pstm = null;
		ResultSet rs = null;
		List<Member> members = null;
		
		try {
			conn = dataSource.getConnection();
			pstm = conn.prepareStatement(sql);
			rs = pstm.executeQuery();
			members = new ArrayList<Member>();
			
			while(rs.next()) {
				Member member = new Member();
				member.setId(rs.getInt("id"));
				member.setName(rs.getString("name"));
				members.add(member);
			}	
			
			
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			try {
				
			rs.close();
			pstm.close();
			conn.close();
			} catch( SQLException e) {
				e.printStackTrace();
			}
		}
		
		return members;
	}
}    
  • 회원목록 출력

3. DB를 이용한 방법 - JPA

  • JPA ( Java Persistence API )
  • JPA는 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해준다.
  • JPA를 사용하면, SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환 할 수 있다.
  • JPA를 사용하면 개발 생산성을 크게 높일 수 있다.
  • JPA는 자바 영역에서 ORM ( Object-Relational Mapping ) 기술 표준으로 사용되는 인터페이스 모음이다.
  • 즉, 실제적으로 구현된 것이 아니라 구현된 클래스와 매핑을 해주기 위해 사용되는 프레임워크이다.

1. build.gradle 수정

  • dependencies 수정
  • jdbc 주석, jpa 추가
  • 수정후 build.gradle 우클릭 > gradle > Refresh gradle project
...
	//implementation 'org.springframework.boot:spring-boot-starter-jdbc'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

...

2. application.properties 수정

  • jpa 추가
...
#jpa
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

3. Member.java 수정

  • Member.java 수정
    • @Entity: jpa가 관리하는 class
    • @Id: pk 설정
@Entity
public class Member {

	@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySequence")
	@SequenceGenerator(name ="mySequence", sequenceName = "member_seq", allocationSize = 1)
	private int id;

4. JpaMemeberRepository.java 생성

  • JdbcMemberRepository 수정
    • @Repository 주석처리
//@Repository
public class JdbcMemberRepository implements MemberRepository{
...
}
  • MemberService.java 수정
    • @Transactional
@Service
@Transactional
public class MemberService {
...
}
  • repository 패키지 > JpaMemeberRepository.java 생성
    • @Repository
    • JPA는 EntityManager로 동작 한다.
    • select m from Member m : JPQL 이라는 쿼리
    • JPQL ( Java Persistence Query Language)
      - 테이블이 아닌 엔티티 객체를 대상으로 검색하는 객체지향 쿼리
      - SQL 을 추상화해서 특정 데이터베이스 SQL에 의존하지 않음
      - JPA는 JPQL을 분석 후 적절한 SQL을 만들어 데이터베이스를 조회
@Repository
public class JpaMemberRepository implements MemberRepository{

	private final EntityManager em;
	public JpaMemberRepository(EntityManager em) {
		this.em = em;
	}
    
    @Override
	public Member save(Member member) {
		em.persist(member);
		return member;
	}
  
  	@Override
	public List<Member> findAll() {
		return em.createQuery("select m from Member m", Member.class).getResultList();
	}
}    
  • 콘솔

    • 가입
    insert into member (name, id) values (?, ?)
    • 목록
     select 
         member0_.id as id1_0_ ,
         member0_.name as name2_0_
     from member member0_
profile
코딩공부

0개의 댓글