카테고리 없음

Spring(4) - 스프링 입문(Spring boot, 웹 MVC, DB접근 기술)

HOONY_612 2021. 6. 30. 16:54
반응형

이번에는 인프런에서 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 강의를 들었다.

내용도 진짜 좋았고 우아한 형제들의 CTO이신 "김영한"님께 정말 감사드린다.

이런 강의를 무료로 들을 수 있었다는 것은 진짜 행운이다. 전체적인 백엔드에 대해서 알아갈 수 있었다.

그리고 현재는 다음 버전인 스프링의 기초 강의를 사서 듣는 중이다. 전에 들었던 강의들을 총 정리 해보겠다.

 

1. Spring Boot

스프링 부트란 스프링 프레임워크를 도와준다. 구체적으로는 다양한 설정들을 미리 설정해놔준다.

처음 Spring을 배우면서 XML파일을 수정해가며 라이브러리를 넣고 톰캣(Tomcat)이라는 서버를 실행해놓고

코드를 작성하여 너무 불편하였다.

그러나 스프링 부트를 사용하면서 모든 것이 한꺼번에 처리가 된다는 게 큰 장점이다.

https://start.spring.io/ 홈페이지에 들어가면 설정을 할 수 있고 프로젝트를 생성해준다. 그 다음 압축을 풀어

IDE환경에서 Import를 할 수 있다. 그리고 컴파일러는 Maven이 아닌 Gradle을 사용한다.

그리고 bundle.gradle파일모든 설정들(Library, Java version)을 확인하고 수정할 수 있다.

 

2. ThymeLeaf Library의 활용

일단 ThymeLeaf Library를 라이브러리로 사용한다. 이것은 템플릿 엔진이라는 것이다.

템플릿 엔진은 정적인 HTML파일이 아닌 동적인 HTML파일을 만들기 위해서 사용하는 것이다.

왜냐면 DB에서 직접 데이터를 가져와 그 데이터를 HTML파일에 넣어서 클라이언트에게 보내주기 위해서이다.

 

간단한 예제를 만들어보겠다.

 

src/main/controller/HomeController.java

@Controller
public class HelloController {
   @GetMapping("hello")
   public String hello(Model model) {
   		model.addAttribute("data", "hello!!");
   		return "hello";
   }
}

위의 클래스를 만들자. GetMapping Annotation이란 URL을 접근했을 경우 아래의 메소드가 실행된다.

그리고 model.addAttribute("Key","Value")는 리턴 값인 "hello.html"에 접근하여 data 변수에 "hello!!"를 집어넣는다.

 

resource/templete/hello.html

<html xmlns:th="http://www.thymeleaf.org">
  <body>
      <p th:text="'hello ' + ${name}">hello! empty</p>
  </body>
</html>

위와 같이 return값에 .html이 붙혀져서 나오는 것은 ViewResolver때문이다.

그러나 그냥 내용만 출력하고 싶다면? 아래와 같이 작성하면 된다.

@Controller
public class HelloController {
   @GetMapping("hello-string")
   @ResponseBody
   public String helloString(@RequestParam("name") String name) {
   		return "hello " + name;
   }
}

이렇게 작성하고 localhost:8080/hello-string?(넣고싶은값)을 URL로 치면 ViewResolver를 거치지 않고 HTTP BODY부에 내용을 실어 클라이언트에게 보낸다.

 

3. 회원 관리 예제

이젠 직접 Spring을 사용하여 구현을 해보겠다.

큰 그림은 첫 번째로는 DB에 접근하지 않고 메모리 상에 데이터를 저장하여 Test해보겠다.

일단 구현하고 싶은 기능은 회원 조회(Read) 및 등록(Create)이다. 아래의 그림은 최종적인 디렉토리의 모습이다.

 

 

일단 개념적으로 깊이 있게 공부하기보단 전체적인 구조를 알고 들어가는게 좋아보여 위의 그림을 만들었다.

첫 번째로 컨트롤러이다. URL을 HTML파일과 맵핑시켜 Thymeleaf라이브러리를 이용해 데이터를 바꾼다.

두 번째로 도메인이다. 도메인은 주요 파라미터인 Member라는 객체를 만들고 그 안에 id와 name을 넣었다.

마지막으로 RepositoryService이다. DB에 접근 할 경우 Service를 거처 Repository로 간다.

그래서 주요 메소드(회원가입, 중복검사 등)Service에 포함된다.

DB에 어떻게 접근을 할 것인가는 Repository가 결정한다. 그리고 Repository인터페이스를 구현하여 5가지 방법으로 구현체를 만들었다.

 

 

4. Junit 테스트

이것은 우리가 만든 메소드들을 DB에 접속하지 않아도 확인해볼 수 있는 방법이다.

현업에서도 각 예외에 대해서 얼마나 테스트 코드를 잘 짜내는지가 정말 중요한 능력이라고 수업에서 들었다.

그래서 테스트 방법에 대해서 알아보자.

 

//given : 이러한 상황에 놓여있을 때

//when : 값들이 이렇게 주어졌을 때

//then : 결과는 어떻게 되냐

 

이런 방법으로 코드를 짜면 된다. 직접 코드를 보자. 자세한 내용은 강의를 들으면 된다.

모든 테스트 코드의 흐름은 값을 지정해주고 그것을 등록하고 다시 조회하여 새로운 객체에 넣는 플로우이다.

@Test
public void 회원가입() throws Exception {
 //Given : hello라고 name을 설정하고
     Member member = new Member();
     member.setName("hello");
 //When : 회원가입을 했을 때
     Long saveId = memberService.join(member);
 //Then : id를 찾는 메소드를 실행한 객체와 같은지 확인
     Member findMember = memberRepository.findById(saveId).get();
     assertEquals(member.getName(), findMember.getName());
}

 

5. Bean의 등록

솔직히 DI에 대해서는 아직 지식이 많이 부족해 글을 쓰진 못하겠다.

등록에 관한 글은 https://hoony-612.tistory.com/17을 을 참고하면 된다.

 

 

6. Controller와 View등록

이번에는 직접 컨트롤러와 뷰를 맵핑시켜준다. 아래의 코드를 보자.

@Controller //자동으로 component - scan가능
public class MemberController {
    private final MemberService memberService;

    @Autowired //스프링 컨테이너에 있는 memberService를 가져다 연결해줌.
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }

    @GetMapping("/members/new")
    public String createForm() {
        return "members/createMemberForm";
    }
    @PostMapping("/members/new")
    public String create(MemberForm form) {
        Member member = new Member();
        member.setName(form.getName());
        memberService.join(member);
        return "redirect:/";
    }

    @GetMapping("/members")
    public String list(Model model) {
        List<Member> members = memberService.findMembers();
        model.addAttribute("members", members);
        return "members/memberList";
    }
}

이렇게 맵핑을 해준다. 그리고 GetPost는 무슨 차이점을 가질까라는 의문점이 들 수 있다.

Get과 Post의 가장 큰 차이점은 내가 보내는 데이터가 URL에 노출이 되냐 안되냐이다.

Post는 노출이 되지 않기에 로그인 구현하는데 적합하다.

그래서 Post를 사용하여 html문서 내에서 form태그 안의 내용을 받아 와 객체에 저장해준다(MemberForm). 그리고 그 데이터로 join함수를 실행하여 데이터를 등록하고 홈 화면으로 돌아간다.

 

 

7. 스프링 DB접근 기술

드디어 대망의 DB접근 기술이다. 이 강의를 들으면서 조금 이해가 안되는 부분이 많았고 유난히 어려웠다.

일단 DB접근 기술은 순수JDBC -> 스프링JdbcTemplete -> JPA -> Spring JPA로 발전되어 왔다.

순서대로 설명하겠다.

 

-  순수 JDBC

일단 순수 JDBC에 접근 할 경우의 코드이다. save메소드를 예시로 가져왔다.

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

코드의 실행은 sql문 작성 -> DB와 connect -> sql문 실행 -> key값 받아오기 -> connection끊기 순이다.

그러나 이 코드를 모든 메소드에 try~catch문을 이용해 적용한다는 것은 비효율적이다.

이러한 문제를 개선한 것이 JdbcTemlplet이다.

 

-  JdbcTemplete

이것도 코드를 일단 참고하자. save메소드의 코드이다.

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public JdbcTempleteMemberRepository (DataSource dataSource){
        jdbcTemplate = new JdbcTemplate(dataSource);
    }

    @Override
    public Member save(Member member) {
        SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(jdbcTemplate); //sql문을 짜지 않아도됨, 만들어줌.
        jdbcInsert.withTableName("member").usingGeneratedKeyColumns("id");
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("name", member.getName());
        Number key = jdbcInsert.executeAndReturnKey(new
                MapSqlParameterSource(parameters));
        member.setId(key.longValue());
        return member;
    }

확연히 코드의 양이 줄어들었다. JDBC의 반복적인 연결을 생성하고 닫고하는 부분을 보완한 라이브러리이다.

하지만 아직까지 SQL문을 직접 작성하여야하는 불편함이 있다. 

 

-  JPA

이것도 코드를 일단 참고하자. save메소드의 코드이다. 이것은 따로 라이브러리를 추가해줘야한다.

 

<resource.application.properties>

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

<bundle.gradle>

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'

<hello.hellospring.domain.Member>

@Entity
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) //DB가 알아서 ID를 생성해주는데 우리가 생성.
    private Long id;
    private String name;
}

<hello.hellospring.repository.JpaMemberRepository>

public Member save(Member member) {
	 em.persist(member);
	 return member;
 }

 

위와 같은 설정을 하고 EntityMember객체에 적용시키면 반복코드는 물론 기본적인 SQL문들을 작성해서 보내준다.

그리고 모든 변경 및 수정은 Transcation안에서 실행되어야한다. Transcation이란 작업 하나하나를 모아둔 패키지라고 생각하면 된다. CRUD의 과정과 똑같다.

 

-  Spring JPA

원래의 JPA에 더욱 확장 되어진 기능을 추가한 것이다.

내가 아는 것은 Id 혹은 Name으로 조회하는 메소드를 Spring JPA 라이브러리에 내장되어져 조회 할 경우 편해진다고 알고있다. Spring JPA에 대해서는 JPA를 깊게 알고 난 후 공부 할 생각이다.

 

8. AOP

AOP에 대해서 알아보자. AOP는 공통 관심 사항을 원하는 곳에 적용하기 위한 프로그래밍 기법이라고 볼 수 있다.

즉, 무언가 재사용해서 사용하고 싶은 모듈을 따로 관리하는 것이다. 

사용방법은 컴포넌트에 @Aspect 애노테이션을 달고 @Around를 메소드에 달아 실행 범위를 설정해준다.

 

 

이렇게 간단하게 Spring이 무엇인지 백엔드가 무엇인지에 대해서 알아봤다.

제일 큰 장점은 직접 내가 DB를 건들이면서 View무언가를 구현할 수 있었다는게 제일 좋다.

이렇게 웹에 대한 지식을 직접 코드로 느껴보면서 더욱 어떻게 공부해야할지 방향성을 잡게 해준 강의였다.

 

 

꼭 추천합니다!!!

 

반응형