스프링부트 너 뭐 돼?🤷‍♀️(2) - SPRING BOOT

joyfulwave·2022년 12월 5일
0

피할 수 없다면 즐기자! 스프링부트 너.. 뭐 돼?




📚 예제 코드

📌 application.properties

# #=> application.properties 의 주석

#port - 포트 수정
server.port=9090

#thymeleaf cache - 타임 리프 캐시 설정
spring.thymeleaf.cache=false

#encoding - 한글 인코딩
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.force=true
server.servlet.encoding.enabled=true

#dbms - jdbc db 설정
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


#jpa - jpa 설정
#spring.jpa.open-in-view=false
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

📌 Member.java (dto)

package com.koreait.core2.member;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;

// @Entity : jpa가 관리하는 class
@Entity
public class Member {
	@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySequence")
	@SequenceGenerator(name = "mySequence", sequenceName = "member_seq", allocationSize = 1)
	private int id;
//	@Column(name="username")
	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;
	}
}

📌 MemberController.java

package com.koreait.core2.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.koreait.core2.member.Member;
import com.koreait.core2.member.MemberFormDTO;
import com.koreait.core2.service.MemberService;

@Controller
public class MemberController {

	// Controller가 Service를 의존한다라고 표현
	// Service는 여러 Controller에서 가져다 쓸 수 있기 때문에
	// Spring Container에 등록을 해야한다.
//	MemberService mService = new MemberService();
	 
	// 스프링스럽게 작업하기
	// service 는 Spring Container에 하나만 생성 및 등록해서 같이 공유해서 쓸 수 있다.
	private final MemberService memberService;
	
	@Autowired
	public MemberController(MemberService memberService) {
		this.memberService = memberService;
	}
	
	/*
	 * 필드 주입(Field Injection)
	 * - final 키워드를 사용할 수 없어, 순환 참조가 발생할 수 있다. 권장하지 않는다.
	 * 
	 * @Autowired private MemberService memberService;
	 */
	
	/*
	 * 수정자 주입 (setter Injection)
	 * - public 으로 노출이 되기 때문에 다른 곳에서 주입 가능하다.
	 * 
	 * private MemberService memberService;
	 * 
	 * @Autowired
	 * public void setMember(MemberService memerService){
	 * 		this.memberService = memberService;
	 * }
	 */
	
	// home.html에서 회원가입 페이지 맵핑
	@GetMapping(value = "/members/new")
	public String createForm() {
		return "members/createMemberForm";
	}
	
	// 회원의 정보를 포스트맵핑
	@PostMapping(value = "/members/new")
	public String create(MemberFormDTO form) {
		// Member => 멤버 dto
		Member member = new Member();
		member.setName(form.getName());
		
		memberService.join(member);
		
		// 홈 화면으로 돌린다.
		return "redirect:/";
	}
	
	@GetMapping("/members")
	public String list(Model model) {
		List<Member> members = memberService.findMembers();
		model.addAttribute("members", members);
		return "members/memberList";
	}
}

📌 MemberService.java

package com.koreait.core2.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.koreait.core2.member.Member;
import com.koreait.core2.repository.MemberRepository;
import com.koreait.core2.repository.MemoryMemberRepository;

@Service
@Transactional
public class MemberService {
//	MemberRepository memberRepository = new MemoryMemberRepository();
	
	private final MemberRepository memberRepository;
	
	
	
	@Autowired
	public MemberService(MemberRepository memberRepository) {
		this.memberRepository = memberRepository;
	}
	
	// 회원가입
	public int join(Member member) {
		memberRepository.save(member);
		return member.getId();
	}
	
	// 전체회원 조회
	public List<Member> findMembers(){
		return memberRepository.findAll();
	}

}

📌 MemberRepository.java - interface

package com.koreait.core2.repository;

import java.util.List;

import com.koreait.core2.member.Member;

public interface MemberRepository {

	// 회원 저장
	Member save(Member member);
	
	// 전체 찾기
	List<Member> findAll();
}

📌 MemmoryMemberRepository.java

package com.koreait.core2.repository;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Repository;

import com.koreait.core2.member.Member;

//@Repository
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());
	}

}

📌 JdbcMemberRepository.java

package com.koreait.core2.repository;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.koreait.core2.member.Member;

//@Repository
public class JdbcMemberRepository implements MemberRepository{

	private final DataSource dataSource;
	
//	@Autowired는 생성자가 하나일 때 생략 가능
	@Autowired
	public JdbcMemberRepository(DataSource dataSource) {
		this.dataSource = dataSource;
	}
	
	@Override
	public Member save(Member member) {
		String sql = "INSERT INTO MEMBER VALUES(MEMBER_SEQ.NEXTVAL, ?)";
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try {
			conn = dataSource.getConnection();
			String generatedColums[] = {"ID"};
			pstmt = conn.prepareStatement(sql, generatedColums);
			pstmt.setString(1, member.getName());
			pstmt.executeUpdate();
			rs = pstmt.getGeneratedKeys();
			
			if(rs.next()) {
				member.setId(rs.getInt(1));
			}
			
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			try {
				rs.close();
				pstmt.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
		return member;
	}

	@Override
	public List<Member> findAll() {
		String sql = "SELECT * FROM MEMBER";
		
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		List<Member> members = null;
		
		try {
			conn = dataSource.getConnection();
			pstmt = conn.prepareStatement(sql);
			rs = pstmt.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();
				pstmt.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
		return members;
	}

}

📌 JpaMemberRepository.java

package com.koreait.core2.repository;

import java.util.List;

import javax.persistence.EntityManager;

import org.springframework.stereotype.Repository;

import com.koreait.core2.member.Member;

@Repository
public class JpaMemberRepository implements MemberRepository{
	
	// JPA는 EntityManager로 모든지 동작 한다.
	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();
		
		// select m from Member m : JPQL이라는 쿼리
	}
	
	

}



📚 MemberController

  • MemberController가 생성될 때, 생성자를 호출해줘요.
  • 즉, Service까지 생성해서 자동으로 호출해줘요.
  • @Autowired를 선언해주면 MemberController가 생성하면 스프링이 memberService와 연결을 해줘요.
  • 기존은 테스트를 통해서만 service의 오류 발생을 확인 할 수 있지만, 서버 기동시에 연결 실패시 에러가 발생해요.
  • 생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다. 이렇게 의존 관계를 외부에서 넣어주는 것을 DI(Dependency Ingection), 의존성 주입이라고해요.
  • 이전에 개발자가 직접 주입했고, 여기서는 @Autowired에 의해 스프링이 주입해줘요.



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

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



📚 스프링 컨테이너란?

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



📚 POJO란?

  • Plain Old Java Object, 단순한 자바 오브젝트

📌 POJO란,

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

⚫ 예

public class UserDTO {
	
	private String userName;
	private String userId;
	private String userPassword;
	
	public String getUserName() {
		return userName;
	}
	
	public void setUserName(String userName) {
		this.userName = userName;
	}
	
	public String getUserId() {
		return userId;
	}
	
	public void setUserId(String userId) {
		this.userId = userId;
	}
	
	public String getUserPassword() {
		return userPassword;
	}
	
	public void setUserPassword(String userPassword) {
		this.userPassword = userPassword;
	}

	
}



📚 의존성 주입(DI, Dependency Injection)

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



📚 SOLID

  • 클린코드로 유명한 로버트 마틴이 좋은 객체지향 설계의 5가지 원칙을 정리

📌 SRP

단일 책임 원칙(Single Responsibility Principle)

  • 한 클래스는 하나의 책임만 가져야 한다.
  • 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것

📌 OCP

개방-폐쇄 원칙 (Open/Closed Principle)

  • 확장에는 열려있고, 수정·변경에는 닫혀있다.

📌 LSP

리스코프 치환 원칙(Liskov Substitution Principle)

  • 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스터스로 바꿀 수 있어야 한다.
  • 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것

📌 ISP

인터페이스 분리의 원칙(Interface Segregation Principle)

  • 범용 인터페이스 하나보다 인터페이스 여러개가 낫다.
  • 인터페이스가 명확해지고, 대체 가능성이 높아진다.

📌 DIP

의존 관계 역전 원칙(Dependency Inversion Principle)

  • 프로그래머는 추상화에 의존해야지, 구체화에 의존해서는 안된다.
  • 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻



📚 JPA

  • JPA는 기존의 반복 코드는 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해줘요.
  • JPA를 사용하면, SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환할 수 있어요.
  • JPA를 사용하면 개발 생산성을 크게 높일 수 있어요.

📌 JPA(Java Persistence API)

  • JPA는 자바 영역에서 ORM(Object-Relational Mapping) 기술 표준으로 사용되는 인터페이스의 모음이에요.
  • 즉, 실제적으로 구현된것이 아니라 구현된 클래스와 매핑을 해주기 위해 사용되는 프레임워크에요.



📚 JPQL(Java Persistence Query Language)

  • 테이블이 아닌 엔티티 객체를 대상으로 검색하는 객체지향 쿼리
  • SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않아요.
  • JPA는 JPQL을 분석 후 적절한 SQL을 만들어 데이터베이스를 조회해요.



무사히 적응할 그 날을 기대 ✔️




출처
https://media.giphy.com/media/kyUIknbbDNvID5XzU4/giphy.gif
https://media.giphy.com/media/A6aHBCFqlE0Rq/giphy.gif

0개의 댓글