728x90

💡 JDBC란 




JDBC(Java Database Connectivity)자바 프로그램과 데이터베이스를 연결하여 데이터베이스와의 상호 작용을 가능하게 하는 자바 API(응용 프로그래밍 인터페이스)입니다. JDBC는 데이터베이스에 접속하고 쿼리를 실행하며, 데이터를 검색하고 수정하는 등의 작업을 수행할 수 있습니다.

자바는 기본적으로 DB와 연결지어지려면 JDBC가 필요합니다.

애플리케이션에서 DB에 연동을 해서 데이터를 기존처럼 메모리에 저장하는 것이 아니라 DB에 저장될 수 있도록 하는 것입니다.

Spring과 DB를 연결하기 위해 H2 데이터 베이스를 사용할 예정인데, 해당 DB 설치관련내용은 아래의 링크에서 확인하여 설치를 진행해주시면 됩니다.

우선 JDBC와 h2 database를 사용하려면 build.grale → dependencies 항목에 라이브러리를 추가해주어야 합니다. https://memory-dev.tistory.com/entry/KimSprInt13

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

그리고 repository 폴더에 JdbcMemberRepository를 생성하여 각각의 코드를 작성해 줍니다. Service폴더에는 SpringConfig파일에 DataSource를 생성합니다.

 

 

 

더보기 버튼을 누르시면 JdbcMemberRepository 코드가 보여집니다. (너무 길어 접어서 작성해두었습니다.)

더보기
        package hello.hellospring.repository;

        import hello.hellospring.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();
                    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<Member> 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<Member> 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<Member> 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<Member> 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);
            }
        }

 

        package hello.hellospring.service;

        import hello.hellospring.repository.JdbcMemberRepository;
        import hello.hellospring.repository.MemberRepository;
        import hello.hellospring.repository.MemoryMemberRepository;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.Configuration;

        import javax.sql.DataSource;

        @Configuration
        public class SpringConfig {
            //스프링이 실행될 때, Configuration을 읽고 하단의 코드를 스프링 빈에 등록하라고 인식하여 등록을 진행합니다.
            //멤버 서비스와 멤버 리포지토리를 스프링 빈에 등록해주고, 스프링빈에 등록되어있는 멤버 리포지토리를 멤버 서비스에 넣어줍니다.

            private DataSource dataSource;
            @Autowired
            public SpringConfig(DataSource dataSource) { this.dataSource = dataSource; }

            @Bean
            public MemberService memberService() {
                return new MemberService(memberRepository());

            }

            @Bean
            public MemberRepository memberRepository() {
                //return new MemoryMemberRepository();
                return new JdbcMemberRepository(dataSource);
            }


        }

 

 

 

h2 DB에서 id와 name을 insert하여 3개의 데이터를 만들어 두었습니다. H2 DB와 스프링이 잘 연결되었는지 확인하기 위해 , 스프링을 실행하여 member url에서 회원 목록을 확인해보겠습니다.

 

 

서버를 실행한 후 회원 목록을 클릭하면 H2 서버 데이터에서 입력해두었던 id, name 데이터가 저장되어 있는 걸 확인할 수 있습니다. 다시 home 브라우저로 돌아간 후 이름을 입력한 후 회원 목록을 확인하면 추가가 되어있고, h2 database에서 table을 조회하면 브라우저에서 등록된 이름 데이터가 저장되어 있는 걸 확인할 수 있습니다.

 

🎈 DB와 연동된 MemberService 이용하여 Test코드 작성해보기

 

👉 통합 TEST 진행해보기

데이터 베이스는 기본적으로 트랜잭션이라는 개념이 있는데 DB에 데이터를 insert를 하게되면 커밋을 진행해야합니다. 테스터가 끝난 후 만약 롤백을 하게된다면 .join 후 검증까지 완료되었는데 롤백하면 DB에서 데이터가 전부 지워지게 됩니다. 여기서 @Transactional을 테스터케이스에 작성해주면 테스트를 실행할 때 트랜잭션을 먼저 실행하고 DB에 데이터를 인서트 한 후 테스터를 마친 후 데이터를 깔끔하게 지워줍니다.

MemberServiceIntegrationTest 코드 (너무 길어 접어서 작성해두었습니다.)

더보기
        package hello.hellospring.service;

        import hello.hellospring.domain.Member;
        import hello.hellospring.repository.MemberRepository;
        import hello.hellospring.repository.MemoryMemberRepository;
        import org.junit.jupiter.api.AfterEach;
        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.*;
        import static org.junit.jupiter.api.Assertions.*;

        @SpringBootTest
        //@Transactional
        class MemberServiceIntegrationTest {

            @Autowired MemberService memberService;
            @Autowired MemberRepository memberRepository;



            //회원가입 서비스가 제대로 작동되는 지 확인해보기
            //test는 한글로 적어도 괜찮다
            @Test
            void 회원가입() {

                //given(어떤데이터) : 테스트 코드를 작성할 땐 어떠한 상황에 주어지는데
                Member member = new Member();
                member.setName("spring");

                //when : 해당 상황을 실행했을 때 (memberService의 join 메서드를 검증)
                Long saveId = memberService.join(member);

                //then(검증부) : 여기 결과가 주어지도록 코드를 작성한다.
                Member findMember = memberService.findOne(saveId).get();
                assertThat(member.getName()).isEqualTo(findMember.getName());

            }

            @Test
            public void 중복_회원_예외() {
                //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("이미 존재하는 회원입니다.");


                //then
            }


        }


먼저 h2 데이터 베이스에 저장된 기존 데이터들을 DELETE FROM MEMBER로 지워줍니다. 그리고 상단의 테스트 코드에서 회원가입 기능을 테스트할 수 있는 회원가입 메서드를 실행하게되면 테스트가 진행되며 다시 H2 데이터베이스에 join 메서드가 실행되며 setName(”spring”)의 값이 저장됩니다.

 

 

 

이렇게 “spring”이라는 name이 저장된 후 다시 회원가입 테스트 코드를 실행하게 되면, 아래처럼 같은 아이디를 존재하는 회원으로 인식하여 테스트가 진행되지 않는 경우가 발생합니다. 이 얘기는 하나의 name에 대해 테스트를 진행하고나면 다시 테스트를 진행했을 때 다음 테스트의 실행을 할 수 없다는 의미입니다. 이런 상황에서는 delete문을 DB에다가 또 작성하여 Test 메서드가 실행되고 나면 리포지토리에 저장된 데이터들을 지워줘야하는 추가 코드를 작성해주어야 하는데, 스프링 부트에서는 @SpringBootTest와 @Transactional 애노테이션을 사용하여 테스트가 전부 진행된 후 롤백 될 수 있도록 해주는 기능을 사용할 수 있습니다.

📄 @SpringBootTest와 @Transactional은 스프링 프레임워크에서 사용되는 어노테이션입니다.

@SpringBootTest : 스프링 부트 애플리케이션의 통합 테스트를 위해 사용되는 어노테이션입니다. 테스트 클래스에서 @SpringBootTest를 사용하면 스프링 애플리케이션 컨텍스트를 로드하고, 애플리케이션의 빈(Bean)들을 주입받을 수 있습니다. 이를 통해 실제 환경과 유사한 환경에서 테스트를 수행할 수 있습니다.

@Transactional : 데이터베이스 트랜잭션 처리를 위해 사용되는 어노테이션입니다. @Transactional을 메서드나 클래스에 적용하면 해당 메서드 또는 클래스의 실행이 트랜잭션 내에서 수행됩니다. 트랜잭션은 ACID(원자성, 일관성, 고립성, 지속성) 속성을 보장하며, 데이터베이스 조작을 일관성있게 처리할 수 있습니다. 메서드가 성공적으로 완료되면 트랜잭션은 커밋되고, 예외가 발생하면 롤백됩니다.

롤백(Rollback)트랜잭션에서 이전 상태로 되돌리는 작업을 의미합니다. 트랜잭션이란 하나의 논리적인 작업 단위를 의미하며, 여러 개의 데이터베이스 조작(쿼리 실행, 데이터 변경 등)을 포함할 수 있습니다.

트랜잭션은 ACID 원칙을 따르며, 롤백은 이 중 "원자성(Atomicity)" 속성을 보장하기 위한 메커니즘입니다. 원자성은 트랜잭션 내의 모든 데이터베이스 조작이 "전부 성공"하거나 "전부 실패"하는 것을 보장하는 속성입니다. 즉, 트랜잭션 내의 어떤 작업이라도 실패하면 트랜잭션 전체가 실패로 판단되어 이전 상태로 롤백됩니다.

롤백은 트랜잭션 내의 데이터 변경을 취소하고, 이전 상태로 복구하는 작업을 수행합니다. 데이터베이스 조작이 실패하거나 예외가 발생한 경우에 주로 롤백이 발생하며, 데이터 일관성을 유지하고 데이터베이스의 무결성을 보장하기 위해 사용됩니다.

예를 들어, 트랜잭션 내에서 A와 B라는 두 개의 데이터베이스 조작이 수행되는 경우를 생각해보겠습니다. 만약 B 조작이 실패하여 예외가 발생하면, 롤백이 실행되어 A 조작 역시 취소되고 이전 상태로 복구됩니다. 따라서 트랜잭션은 일관된 상태를 유지하게 됩니다.

롤백은 데이터베이스 관리 시스템(DBMS)에 의해 제공되는 기능으로, 트랜잭션의 시작과 끝을 명시적으로 정의하거나, 프레임워크에서 자동으로 처리될 수 있습니다. 롤백은 데이터 일관성과 안전한 데이터 조작을 위해 중요한 개념으로 사용됩니다.

728x90
반응형
728x90

 

Spring Bean (스프링 빈)이란❓


Spring Bean은 Spring Framework에서 관리되는 객체를 의미합니다. 의존성 주입(Dependency Injection, DI)객체 간의 의존 관계를 개발자가 직접 관리하지 않고, 외부에서 의존하는 객체를 주입하여 사용하는 디자인 패턴입니다.
DI는 객체 간의 결합도를 낮추고 유연하고 재사용 가능한 코드를 작성할 수 있도록 도와줍니다.
Spring Framework은 의존성 주입(Dependency Injection)을 통해 객체들을 생성, 관리, 연결하는데, 이러한 객체들을 “Spring Bean”이라고 합니다.

Spring Bean은 일반적으로 자바 클래스로 표현되며, Spring 컨테이너(ApplicationContext)에 의해 생성되고 관리됩니다. Spring Bean은 개발자가 정의한 설정 파일(XML 또는 Java Config)에 기반하여 컨테이너에 등록됩니다. 컨테이너는 Spring Bean의 생명 주기를 관리합니다.

 

Spring Bean의 주요 특징:

  1. 스프링 컨테이너에 등록: Spring Bean은 스프링 컨테이너에 등록되어야 합니다. 등록된 Bean은 컨테이너에서 관리되고 필요한 곳에서 주입(Dependency Injection)되어 사용됩니다.

  2. 라이프사이클 관리: Spring 컨테이너는 Spring Bean의 라이프사이클을 관리합니다. Bean의 생성, 초기화, 소멸 등을 컨테이너가 담당합니다.

  3. 의존성 주입: Spring Bean은 의존성 주입(Dependency Injection)을 통해 다른 Bean과 연결됩니다. 이를 통해 객체 간의 결합도를 낮출 수 있고, 유연하고 재사용 가능한 애플리케이션을 만들 수 있습니다.

  4. 스코프: Spring Bean은 스코프에 따라 다르게 관리됩니다. 주요 스코프로는 Singleton, Prototype, Request, Session 등이 있으며, 각 스코프에 따라 Bean의 생성과 소멸 시점이 달라집니다.

  5. 어노테이션과 XML 설정: Spring Bean은 어노테이션을 사용하여 자동으로 등록할 수도 있고, XML 설정 파일을 통해 수동으로 등록할 수도 있습니다. 주로 XML 설정 방식은 과거에 사용되었으며, 현재는 주로 어노테이션을 사용하는 방식이 일반적입니다.

 

일반적으로 객체 간의 의존 관계는 하나의 객체가 다른 객체를 생성하거나 참조하여 사용하는 것으로 나타납니다. 이러한 의존 관계는 코드 내에서 강한 결합도를 만들어내고, 객체의 생성과 관리를 담당하는 클래스가 다른 객체들에 대한 정보를 갖고 있어야 한다는 문제를 발생시킵니다. 이로 인해 코드의 유연성과 재사용성이 저하되는 문제가 발생할 수 있습니다.

DI는 이러한 문제를 해결하기 위해 의존하는 객체를 외부에서 주입하는 방식을 채택합니다. 주입된 객체는 해당 객체가 필요한 시점에 사용됩니다. 주로 생성자(Constructor) 주입, Setter 주입, 인터페이스 주입 등의 방식을 사용합니다.

 

        package hello.hellospring.controller;


        import hello.hellospring.service.MemberService;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Controller;

        //스프링이 처음에 뜰 때, 스프링 컨테이너라는 박스가 생기는데 만약 그곳에 컨트롤러 애노테이션이 있으면 
	//MemberController객체를 생성해서 담아준 후 스프링 빈이 관리해줍니다.
        @Controller
        public class MemberController {

            1. DI(Dipendency Injection) 필드 주입 방식
            @Autowired private MemberService memberService();


            2.DI(Dipendency Injection) setter 방식
            private MemberService memberService;

            @Autowired
            public void setMemberService(MemberService memberService) {
                this.memberService = memberService;
            }


            3.DI(Dipendency Injection) 생성자 주입 방식

            private final MemberService memberService;

            @Autowired
            public MemberController (MemberService memberService) {
                this.memberService = memberService;
            }
        }

 

DI의 주요 이점은 다음과 같습니다:

  1. 결합도 감소: 객체 간의 의존 관계를 외부에서 관리하기 때문에 객체들 간의 결합도가 낮아집니다. 이로 인해 하나의 객체 수정이 다른 객체에 미치는 영향을 최소화하고, 코드의 유지 보수성이 향상됩니다.

  2. 재사용성과 확장성: DI를 통해 의존하는 객체를 외부에서 주입받기 때문에 해당 객체의 변경 없이 쉽게 다른 객체와 조합하여 재사용하거나 기능을 확장할 수 있습니다.

  3. 테스트 용이성: 의존 객체를 모의(Mock) 객체로 대체하여 단위 테스트를 수행할 수 있습니다. 의존 객체의 동작을 외부에서 주입하여 테스트를 진행할 수 있기 때문에 테스트 용이성이 높아집니다.

Spring Framework는 DI를 통해 객체 간의 의존 관계를 관리하고 주입하는 기능을 제공합니다.
이를 통해 Spring에서는 객체 지향적인 애플리케이션을 개발할 수 있으며, 유연하고 확장 가능한 코드를 작성할 수 있습니다.

728x90
반응형
728x90

 

🎈 비즈니스 요구사항 정리


단순한 비즈니스 로직에 대해서 진행해보기 데이터 : 회원 ID, 이름 기능 : 회원 등록, 조회 → 아직 데이터 저장소가 선정되지 않음 (가상의 시나리오) :아직 db가 선정되지 않은 상황에서 개발을 진행해야 하는 상황입니다.

  • 일반적인 웹 애플리케이션의 계층구조
    • 컨트롤러 : Web MVC의 컨트롤러 역할
    • 서비스 : 핵심 비즈니스 로직 구현
    • 리포지토리 : 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리
    • 도메인 : 비즈니스 도메인 객체, 예) 회원, 주문, 쿠폰 등 주로 데이터 베이스에 저장하고 관리됨

 

💡 웹 개발에 필요한 기본방법

✅ 1. 정적 컨텐츠 (static content)


스프링(Spring) 프레임워크에서 자동으로 제공되는 정적 컨텐츠(static content)는 웹 애플리케이션에서 동적으로 생성되지 않고 고정된 형태로 제공되는 리소스를 말합니다.
이는 주로 HTML, CSS, JavaScript, 이미지 파일 등과 같은 클라이언트 측에서 사용되는 파일들을 의미합니다.

정적 컨텐츠는 웹 애플리케이션의 리소스로 사용되며, 웹 브라우저에 직접 제공됩니다. 일반적으로 정적 컨텐츠는 웹 애플리케이션의 클래스패스 내에 위치하며,
클라이언트 요청에 따라 해당 리소스가 서버로부터 전달되어 브라우저에 표시됩니다.

스프링에서 정적 컨텐츠를 제공하기 위해 ResourceHttpRequestHandler와 같은 핸들러를 사용합니다.
이 핸들러는 요청된 URI와 일치하는 정적 리소스를 찾아서 응답으로 전송합니다.
스프링은 클래스패스, 파일 시스템, 웹 JAR 파일 등 다양한 위치에서 정적 리소스를 찾을 수 있도록 지원합니다.

정적 컨텐츠는 스프링 MVC에서 웹 애플리케이션의 리소스를 제공하는 중요한 요소 중 하나이며, 클라이언트에게 웹 페이지의 디자인, 스타일, 스크립트 등을 제공하는 데 사용됩니다.

 

 

 

🎈 2. MVC(Model-View-Controller) 소프트웨어 디자인 패턴 & 템플릿 엔진


MVC(Model-View-Controller)는 소프트웨어 디자인 패턴으로, 애플리케이션의 구성 요소를 3가지 모델(Model), 뷰(View), 컨트롤러(Controller)로 분리하여 개발하는 방법론입니다.
각 구성 요소는 특정한 역할과 책임을 가지며, 애플리케이션의 유지 보수성, 재사용성, 확장성을 높이는 데 도움이 됩니다.

  1. 모델(Model): 데이터와 비즈니스 로직을 담당합니다. 데이터베이스에서 데이터를 가져오거나 수정하고, 데이터의 유효성을 검증하며, 비즈니스 로직을 수행합니다.
    모델은 도메인 객체, 데이터 액세스 객체, 서비스 객체 등으로 구성될 수 있습니다.

  2. 뷰(View): 사용자 인터페이스를 담당합니다. 모델의 데이터를 사용자에게 보여주고, 사용자 입력을 받아 컨트롤러에 전달합니다.
    뷰는 주로 HTML, CSS, JavaScript 등을 사용하여 웹 애플리케이션의 UI를 구성합니다. 사용자가 보는 화면을 담당하는 역할을 수행합니다.

  3. 컨트롤러(Controller): 모델과 뷰를 연결하고, 사용자의 요청을 처리하는 역할을 합니다. 사용자의 요청을 받아 해당 요청에 대한 작업을 수행하고,
    그 결과를 모델에 전달하여 업데이트하거나, 뷰에 전달하여 사용자에게 응답합니다. 컨트롤러는 사용자의 입력을 처리하고, 모델과 뷰 간의 상호작용을 조정하는 중개자 역할을 합니다.

템플릿 엔진을 MVC 방식으로 쪼개서 뷰를 템플릿 엔진으로 html을 더 프로그래밍 후 렌더링이 된 html을 클라이언트에게 전달해주는 것이 템플릿 엔진 방식이라고 합니다.

  • 템플릿 엔진(Template Engine)은 동적인 컨텐츠를 생성하는 데 사용되는 도구입니다. 템플릿 엔진은 템플릿 문서와 데이터를 결합하여 최종 결과물을 생성합니다. 주로 웹 애플리케이션에서 사용되며, HTML, XML, JSON 등 다양한 형식의 템플릿을 처리할 수 있습니다.
  • 템플릿 엔진은 템플릿 문서에 동적인 데이터를 삽입하고, 조건문, 반복문 등을 사용하여 동적인 로직을 처리할 수 있습니다. 예를 들어, 사용자가 요청한 데이터를 템플릿 엔진에 전달하면 엔진은 템플릿 문서 내에서 해당 데이터를 사용하여 최종 결과물을 생성하고 클라이언트에 전송합니다.
  • 스프링 프레임워크에서는 여러 가지 템플릿 엔진을 지원합니다. 대표적으로 Thymeleaf, Freemarker, Velocity, JSP 등이 있습니다. 이러한 템플릿 엔진을 사용하여 MVC 패턴에서 뷰를 생성하고 데이터를 바인딩하여 동적인 웹 페이지를 구성할 수 있습니다.
728x90
반응형
728x90

 

Welcome Page를 만들어보겠습니다. 프로그램이 실행될 때, 가장 먼저 실행되어 출력되는 Welcome page를 만들기 위해서는
src 폴더 / resources / static으로 접근하여 static에 index.html file을 생성합니다. (오른쪽 우클릭 후 나오는 file 생성 메뉴)

생성 후 출력물을 띄우기 위해서 간단한 html 코드를 작성 후 HelloSpringApplication을 실행 하고 , localhost:8080로 접속하면 작성된 Hello가 출력되는 지를 확인할 수 있습니다.

💡 Spring Boot가 제공하는 Welcome Page 기능


Spring은 실행 시 static폴더 내에 있는 index.html파일을 찾아서 동작하는 기능을 제공합니다.
이 때의 static에 있는 index.html은 그냥 화면에 입력된 마크다운 언어를 띄어주기만 하는 정적페이지인데,
템플릿 엔진을 사용하면 해당 파일을 동적으로 변화시킬 수가 있습니다.
- 스프링 공식 튜토리얼 : https://spring.io/guides
- 스프링 부트 메뉴얼 : https://docs.spring.io/spring-boot/docs/current/reference/html/

 

 

Spring Boot Reference Documentation

The reference documentation consists of the following sections: Legal Legal information. Getting Help Resources for getting help. Documentation Overview About the Documentation, First Steps, and more. Getting Started Introducing Spring Boot, System Require

docs.spring.io

 

hello.hellospring 안에 controller 패키지를 생성 후 HelloController 클래스를 생성

 

package hello.hellospring.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HelloController {

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

 

먼저 package와 class 사이 공간에 @controller를 입력해주면 자동으로 상단에 import org.springframework.stereotype.Controller; 해당문구가 입력되며 Controller가 import 됩니다.

 

웹 브라우저에서 http://localhost:8080/hello로 입력하면 스프링 부트는 톰캣이라는 웹 서버를 내장하고 있는데,
그 서버에서 입력된 주소를 스프링으로 전달해주는데, helloController 클래스 내부에 작성된 @GetMapping(”hello”)로 인해 hello가 url에 매칭을 확인합니다.
public String hello(Model model) { model.addAttribute("data","Hello!!"); return "hello"; } → 해당 메서드를 실행하게 됩니다.

이 후 스프링이 Model을 생성하여 model에는 data:hello!! 담아두었습니다. return은 resources안에 templates내에 이름이 “hello”인 파일을 실행하게됩니다. 즉, templates/hello.html을 실행하게 되는 것입니다. ${data} → model에서 넘겨진 key값이다 (=hello!!)

 

💡 ViewResolver (뷰 리졸버)

View Resolver(뷰 리졸버)는 웹 애플리케이션에서 사용되는 MVC 패턴에서 컨트롤러가 처리한 결과를 어떤 뷰(View)로 보여줄지를 결정하는 역할을 합니다.

일반적으로 MVC 패턴에서 컨트롤러는 비즈니스 로직을 처리한 후에 클라이언트에게 결과를 보여줄 뷰를 선택해야 합니다.
이때 View Resolver는 컨트롤러의 처리 결과와 매핑되는 뷰를 찾아주는 역할을 수행합니다.

View Resolver는 컨트롤러가 반환한 뷰의 이름을 기반으로 실제 뷰 객체를 찾아내는 과정을 수행합니다.
이를 통해 컨트롤러와 뷰 사이의 강한 결합을 피하고, 유연한 뷰 선택을 가능하게 합니다.
또한 View Resolver는 뷰의 경로나 포맷에 대한 설정을 담당하여 개발자가 뷰의 경로를 하드코딩하는 것을 피할 수 있도록 합니다.

일반적으로 View Resolver는 설정 파일에 등록되며, 컨트롤러에서 반환하는 뷰의 이름을 기반으로 해당하는 뷰 객체를 찾아내는 방식으로 동작합니다.
View Resolver는 컨트롤러와 뷰 간의 인터페이스 역할을 수행하여, 컨트롤러의 처리 결과를 적절한 뷰로 매핑하여 클라이언트에게 보여줄 수 있도록 도와줍니다.

Spring Framework에서는 다양한 유형의 View Resolver를 제공하며, 개발자는 필요에 따라 해당 View Resolver를 선택하고 설정할 수 있습니다.
이를 통해 Spring MVC에서는 컨트롤러와 뷰 간의 느슨한 결합을 유지하면서 유연하고 확장 가능한 웹 애플리케이션을 개발할 수 있습니다.

 

작성한 project file을 터미널에 ./gradlew.bat build를 통해 빌드 후 실행을 확인합니다.
빌드 폴더 생성 시에는 경로로 설정 → PS D:\backend\Spring\hello-spring>./gradlew.bat build하면 빌드폴더가 생성되고
이 후 빌드를 지우고 싶으면 다시 이전 경로로 이동 후 ./gradlew build clean을 입력해주면 build 폴더 내에 build 되었던 데이터가 지워지게 됩니다.

728x90
반응형
728x90

 

💡 Java 11 설치하고 통합개발환경도구로는 intelliJ 또는 Eclipse를 설치한다.

 

Project는 개발에 필요한 라이브러리를 불러와주고 빌드 라이프사이클까지 관리해주는 툴인 Gradle or Maven 둘 중에 하나를 골라서 사용하는데 일반적으로 Gradle을 사용해서 진행합니다.

언어는 JAVA를 선택한 후 진행하고, SpringBoot는 버전을 선택하는데 뒤에 SNAPSHOT(아직 만들고 있는 것) 이나 M2 (정식 릴리즈버전이 아님) 가 붙은 임시 버전을 제외하고 아무 버전이나 사용해도 무방합니다.
3.0 이상 부터는 자바 17이상 지원하기에 자바를 또 설치하셔야 합니다. 2.7.10 으로 선택해서 진행해도 괜찮습니다.

Group - 기업 도메인 명을 작성하나, 연습용으로는 아무거나 괜찮습니다.
Artifact - 릴리즈 될때의 결과물인데 프로젝트명과 유사한 의미입니다.
Dependencies - 프로젝트 시작 시 어떤 라이브러리를 불러와서 사용할 것인가를 고르는 것입니다.
Spring Web과 Thymeleaf를 2개의 라이브러리를 골라서 선택합니다.
Generate로 다운로드를 받은 후 압축을 푼 후 intelliJ에서 build.gradle 파일을 오픈합니다.

 

src폴더 안에 main폴더와 test폴더가 기본적으로 나누어져서 구조가 이루어져 있다.

728x90
반응형

+ Recent posts