본문으로 건너뛰기

"data" 태그로 연결된 4개 게시물개의 게시물이 있습니다.

모든 태그 보기

· 약 9분
이태환

Supabase란 무엇인가? 웹페이지 SELECT, INSERT 실습

목적

  • Supabase란 무엇인가?
    • Firebase와의 차이점은 무엇인가?
  • 웹페이지에서 데이터를 불러오고 삽입하기
    • 구현하고자 하는 코드
    • 발생한 문제

생활코딩의 Supabase 입문수업 - YouTube를 실습했습니다.

  • 부딪혔던 문제와 해결법을 공유합니다.

Supabase란 무엇인가?

  • 웹과 앱의 백앤드를 구현하기 위한 모든 기능을 제공하는 클라우드 서비스
  • 데이터베이스 : 데이터가 모여져 있는 공간으로 구조적인 방식으로 관리되는 데이터들의 집합
  • 스토리지 : 업로드된 파일을 저장
  • 인증기능 : 회원가입 및 로그인, 로그아웃 등
  • Edge Function : 프로그래밍적으로 다양한 기능 처리 (장고, FastAPI 등이 수행하던)

구글의 Firebase와의 차이점

  • Supabase는 데이터베이스를 관계형 데이터베이스인 PostgreSQL을 사용합니다.

Supabase의 계층

  1. 계정 (Account)
  2. 조직 (Organizations)
  3. 프로젝트 (Projects)
    • 데이터베이스
    • 스토리지
    • 인증기능
    • Edge functions

실습에서 만들고자 하는 서비스

  • 슈파베이스를 통해 데이터베이스를 구축한다.
    • title(text), body(text), create_at(date), id(int8) 컬럼을 만든다.
  • 웹페이지를 통해 구축된 데이터베이스에서 불러와 렌더링한다.
  • 웹페이지의 'Create' 버튼을 이용해서 새로운 'title'과 'body'를 입력받아 데이터베이스로 신규 데이터로 추가한다.

1. Supabase 가입하기

  • 웹페이지에 들어가면, 나오는 대문 내용이 아주 맘에든다.

    • 일주일안에 만들어서, 수백만에게 배포하기
  • 회원가입을 해보자.

  • 나는 GitHub으로 회원가입했다.

2. 프로젝트 만들기

  • 프로젝트를 시작하기 위해서 'Start your project' 버튼을 눌러보자.
  • 그러면 아래와 같은 대쉬보드가 나타난다.

  • 이름을 사용한 기본값 조직이 생성되어 있을 것이다. 이 조직 안에 'New Project' 버튼을 눌러보자.

  • 프로젝트 상위 조직을 지정한다.
  • 프로젝트 이름을 정하고
  • PostgreSQL의 데이터베이스의 암호를 입력한다.
    • 나는 생성기를 통해서 생성했다. (따로 저장해두고 사용할 일을 거의 없다고 한다.)
  • Region은 서비스가 배포될 지역과 가까운 곳으로 설정하자.
    • 나는 한국에 서비스를 해야하니, 서울로 정한다.
  • 위 내용들을 설정하면 이제 프로젝트가 만들어졌다.

3. 데이터 입력하기

  • 프로젝트 생성을 마치면, 위와 같이 대쉬보드가 보입니다.
  • 테이블 데이터를 만들어주기 위해서 '테이블 에디터' 버튼을 눌러줍니다.

  • 'New table' 버튼을 눌러 새 테이블을 만듭시다.

  • 기본값으로는 'RLS'가 활성화 되어있다.

  • 처음에 RLS를 체크를 해제하고 아래와 같이 'page' 테이블을 만들었다.

  • 이렇게 만든 테이블은 누구나에게 접근이 가능하게 된다고 경고가 나온다.
    • RLS을 활성화 하지 않아서 그렇다.

  • 'Insert' 버튼을 누르고, 'Insert row'를 눌러 데이터를 추가해보자.

  • 'id'와 'create_at'은 자동으로 입력된다.
  • 선택 영역인 'title'과 'body'를 예시로 입력하고 저장해보자.

  • 'id'값이 1인 데이터가 하나 만들어졌다.

4. 웹서비스와 연결하기

  • 웹페이지와 데이터베이스를 연결한 서비스를 만든다고 가정해보자.
  • 완성된 예시 코드를 먼저 훑어보자.
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
    <title>JS Bin</title>
  </head>
  <body>
    <h1>Supatodo</h1>
    <div id="history"></div>
    <input type="button" value="create" id="create_btn" />

    <script>
      const supabaseUrl = API의 url
      const supabaseKey = API의 key
      const client = supabase.createClient(supabaseUrl, supabaseKey)
        async function refreshHistory() {
        let { data: record, error } = await client.from('page').select('*');
        console.log('record', record);
        let tag = "";
        for (let i = 0; i < record.length; i++) {
          tag += `<h2>${record[i].title}</h2>${record[i].body}`;
        }
        document.querySelector('#history').innerHTML = tag;
        }
      refreshHistory()

      async function recordHandler() {
        const { data, error } = await client
      .from('page')
      .insert([
        { title: prompt("title?"), body: prompt("body?") }
      ]).select()
        refreshHistory()
      }
      document.querySelector('#create_btn').addEventListener("click", recordHandler);
    </script>
  </body>
</html>

4-1. 웹페이지에 supabase 설치

  • supabase의 도큐먼트 -> Installing을 보면 'package' 또는 'CDN'을 통한 설치 안내가 나와있다.
  • 여기서는 CDN으로 supabase를 설치한다.
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>
//or
<script src="https://unpkg.com/@supabase/supabase-js@2"></script>

4-2. supabase API 연결하기

  • 'API 문서' 탭을 들어가면, 'Introduction' 영역에서 우리가 API를 연결할 수 있는 코드가 제공된다.
  • 웹페이지에서 패키지를 import하는 것이 까다롭기 때문에, 여기서는 사용하지 않는다.
    • 대신에 CDN으로 설치하면 전역변수 supabase를 통해 기능을 접근할 수 있다.
  • 'url'과 'key'가 입력되어야 한다.

  • 이는 '설정 -> API'에 들어가면 볼 수 있다.

4-3. 테이블에서 데이터를 읽어오기

  • 'Introduction' 항목 밑에 우리가 만든 테이블 이름을 고를 수 있다.
  • 밑으로 스크롤을 좀 내리면 'Read rows' 영역에 'READ ALL ROWS'의 코드가 있다.
  • 이 비동기(await) 코드를 async 함수에 넣어서 사용해주면 데이터를 불러와 콘솔에 출력시켜볼 수 있다.
async function refreshHistory() {
    let { data: record, error } = await client.from('page').select('*');
    console.log('record', record);

4-4. 웹페이지 div 태그에 불러온 데이터 삽입하기

  • 데이터를 읽은 수 만큼 할당하는 동적할당을 위해서 for문을 사용한다.
    • for문으로 record를 반복해서 title과 body를 문자열 tag에 담아준다.
    • 완성된 tag를 querySelector()를 통해서 div를 선택햬서, innerHTML에 넣어준다.
let tag = "";
for (let i = 0; i < record.length; i++) {
  tag += `<h2>${record[i].title}</h2>${record[i].body}`;
}
document.querySelector("#history").innerHTML = tag;

4-5 웹페이지에 'create' 버튼으로 데이터베이스에 데이터 추가하기

  • 아까 읽어온 것처럼 'API 문서'의 테이블에 들어가서 'INSERT A ROW'의 코드를 가져온다.
  • 위 코드를 가져와서 async 함수에 넣어준다.
  • 위에서 some_column과 other_column에 우리가 넣고자 하는 데이터베이스의 컬럼 값을 넣어준다.
  • 그리고 'someValue'와 'otherValue'에 넣고자 하는 값을 넣어준다.
    • 여기서는 prompt를 입력받아서 데이터를 삽입해주고 페이지에 랜더링한다.
async function recordHandler() {
  const { data, error } = await client
    .from("page")
    .insert([{ title: prompt("title?"), body: prompt("body?") }])
    .select();
  refreshHistory();
}
document.querySelector("#create_btn").addEventListener("click", recordHandler);
  • 여기까지 supabase 입문에 대해 알아보는 것을 마친다.

결론

  • supabase를 가입
  • 프로젝트를 만들고
  • table을 만들어 웹페이지에서 불러와(SELECT) 랜더링
  • 웹페이지에서 table의 데이터를 삽입(INSERT)

· 약 4분
이태환

폴리움으로 등차지역도 그리기

folium.Choropleth() 메서드를 이용해서 등차지역도를 그릴 수 있다. 서울시 개별공시지가 정보의 데이터를 활용하면, 동별 평균 공시지가를 구할 수 있고, 지정된 데이터의 법정동 이름과 공시지가의 컬럼을 가지고 데이터 만들고, 법정동 명을 키로 해서 지도에 시각화 시키고자 한다.

key_on의 파라미터의 인수로서 geo_data의 컬럼을 지정하는 지 않고, JSON의 형식처럼 "feature.propertiees.bj_nm"을 넣어주었다.

geo_data는 어떻게 생겼길래 이렇게 넣어주는 것인가?

Geo_data는 어떤 녀석인가?

여기에서 나오는 sungdong_gu_dong_gpd는 GeoDataFrame이다.

sungdong_gu_dong_gpd.info()

를 출력해보자. GeoDataFrame이라고 한다. 컬럼들을 갖는게 데이터프레임과 비슷해보인다.

sungdong_gu_dong_gpd.head()

를 출력해보자. 여전히 데이터 프레임과 같아 보인다.

sungdong_gu_dong_gpd.to_json()

으로 JSON 형식으로 바꿔보자.

한 줄로 나와서 보기가 불편하다.

sungdong_gu_dong_gpd.__geo_interface__

으로 출력하니 보기가 좋다.

우리가 찾는 구조의 json형식의 파일이 나온다. 그럼 JSON 자료형인가? __geo_interface__ 에 대해서 알아보자.

__geo_interface__이란 무엇인가?

GeoDataFrame.__geo_interface__ 메서드는 GeoDataFrame을 파이썬의 GeoJSON 같은 FeaturesCollection의 형식으로 반환한다.

folium.choropleth(key_on) 파라미터는 무엇인가?

folium.Choropleth()는 컬러 그레디언트를 사용해서 맵에 데이터를 시각화 하기 위해 사용하는 메서드이다. 메서드의 한 파라미터가 key_on이다. 이 파라미터는 다른 파라미터로 제공된 data와 매칭되어 사용되어질 GeoJSON 데이터의 property를 가리킨다.

위 코드에서 사용한 key_on=feature.properties.bj_nm을 쪼개서 이해해보면

  • feature : folium이 GeoJSON을 구성하고 있는 feature 레벨 데이터에 접근하도록 말해준다.
  • properties : feature와 함께 많이 쓰게 되는 properties는 GeoJSON Feature들로 묶여 저장된 데이터들을 말하며 <GeoJSON_object>의 하나이다.
  • bj_nm : properties로 묶여진 특정 속성의 이름이다.

요약

  • foilum.choropleth()는 geoData와 DataFrame을 연결하여 시각화 한다.
  • 이때, DataFrame은 컬럼, GeoData는 GeoJSON의 property들을 매칭하여 시각화 한다.

· 약 12분
이태환
  • 애플리케이션이 어떻게 동작하는지
  • 어떠한 이유로 이러한 구성을 갖게 되는지
  • 위에 관한 점들을 알고 실습하기 위한 기초지식

서버 간 통신

  • 서버 간 통신은 한 서버에서 다른 서버로 요청하는 것을 의미하는 단어로
  • 통신을 요청하는 클라이언트와 응답을 내려주는 서버 간의 통신을 이야기한다.
  • 가장 흔한 프로토콜이 HTTP/HTTPS 방식이다.

마이크로서비스 아키텍처에서 더 많이 활용된다.

  • 단일 애플리케이션으로 다양한 서비스를 하면 유지보수에 따른 비용이 크다.
  • 따라서, 요즘에는 여러 애플리케이션을 서비스별로 나눠서 개발한다.
  • 하지만 이때문에 각자의 독립적인 애플리케이션 서버간의 통신이 잦아진다.

스프링 부트 동작 방식

  • 스프링 부트에서는 기본적으로 톰캣을 사용하는 스프링 MVC 구조를 기반으로 동작한다.

서블릿

  • 서블릿은 클라이언트의 요청을 처리하고 결과를 반환하는 웹프로그래밍 기술이다.
  • 서블릿 컨테이너가 서블릿 인스턴스를 생성하고 관리한다.

스프링에서 서블릿

  • 스프링에서는 발송 서블렛이 웹 애플리케이션으로 들어오는 모든 요청을 중앙제어하는 프론트 컨트롤러를 구현한다.
  • 발송 서블릿이 들어오는 요청을 받아서 적절한 핸들러에 전달한다.
  • 발송 서블릿이 많은 핸들러 매핑을 사용하여, 각각의 요청을 어떤 핸들러가 처리해야하는지 결정한다.
  • 스픵은임베딩된 톰캣을 내장서버로서 사용하기 때문에, 서블렛, 필터와 애플리케이션을 구성하는 웹 컴포넌트들의 환경설정을 web.xml에 적는다.
  • 즉, web.xml 파일안에 정의된 URL 매핑을 기반으로 적절한 서블릿과 필터로 요청을 보낸다.

스프링 부트의 동작 순서

  1. 발송 서블렛으로 HttpServletRequest의 요청이 들어오면, 요청받은 URL에 매핑된 핸들러 컨트롤러를 탐색한다.
  2. 핸들러 어댑터로 컨트롤러가 호출된다.
  3. 핸들러 어댑터에 컨트롤러의 응답이 돌아오면 ModelAndVew로 응답을 가공해 반환한다.
  4. 뷰 형식으로 리턴하는 컨트롤러를 사용할 때는 뷰 리졸버가 뷰를 반환한다.

핸들러 매핑

  • 핸들러 매핑 인터페이스는 요청 정보를 기준으로 어떤 컨트롤러를 사용할 것인가 결정한다.

핸들러 매핑의 구현체 클래스

BeanNameUrlHandlerMapping

  • 기본설정값의 핸들러 매핑 구현체로
  • 빈의 이름을 요청 URL과 매핑하는데 사용한다.
  • 빈을 정의하면서, 슬래시(/)와 함께 작성된 식별자인 슬러그를 지정하여 매핑한다.
@Bean("/hello")
  • '/hello'라는 슬러그에 빈이 매핑되었다.

  • 이러한 매핑은 직접적인 이름 매칭과 "*"패턴을 사용하는 패턴 매칭을 돕는다.

  • 수신된 URL "/foo"가 '/foo'라고 불리는 빈에 매핑된다.

  • 패턴 매핑은 "/foo" 요청을 '/foo', '/foo2', '/fooOne' 등으로 매칭한다.

  • 아래는 '/beanNameUrl' 요청을 다루는 빈 컨트롤러를 등록하는 소스코드이다.

@Configuration
public class BeanNameUrlHandlerMappingConfig {
    @Bean
    BeanNameUrlHandlerMapping beanNameUrlHandlerMapping() {
        return new BeanNameUrlHandlerMapping();
    }

    @Bean("/beanNameUrl")
    public WelcomeController welcome() {
        return new WelcomeController();
    }
}
  • 이것이 위의 자바 기반 환경 구성과 같은 xml 코드이다.
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
<bean name="/beanNameUrl" class="com.baeldung.WelcomeController" />

  • 이러한 환경설정 둘 다, BeanNameUrlHandlerMapping에 관한 빈을 정의하는 것이 필수가 아니다.
  • 스프링 MVC에서 다 제공된다.
  • 빈 정의를 지우는 것이 문제를 발생시키지 않기에, 요청들이 등록된 핸들러 빈에 여전히 매핑될 것이다.
  • '/beanNameUrl'의 모든 요청이 발송 서블릿에 의해서 'WelcomeController'로 보내진다.
  • 그러면 'WelcomeController'는 'welcome'이라는 뷰를 반환한다.
  • 아래의 코드를 통해 환경설정을 테스트하고 올바른 뷰 이름을 반환하는지 확인하자.
public class BeanNameMappingConfigTest {
    // ...

    @Test
    public void whenBeanNameMapping_thenMappedOK() {
        mockMvc.perform(get("/beanNameUrl"))
          .andExpect(status().isOk())
          .andExpect(view().name("welcome"));
    }
}

SimpleUrlHandlerMapping

  • SimpleUrlHandlerMapping은 좀 더 유연한 핸들러 매핑 구현체이다.
  • 이 매핑은 빈 객체와 URL 간 또는 빈 이름과 URL 간의 직접적이고 선언적인 매핑이다.
  • "/simpleUrlWelcome"과 "/*/simpleUrlWelcome" 요청을 'welcome' 빈으로 매핑해보자.
@Configuration
public class SimpleUrlHandlerMappingConfig {

    @Bean
    public SimpleUrlHandlerMapping simpleUrlHandlerMapping() {
        SimpleUrlHandlerMapping simpleUrlHandlerMapping
          = new SimpleUrlHandlerMapping();
        
        Map<String, Object> urlMap = new HashMap<>();
        urlMap.put("/simpleUrlWelcome", welcome());
        simpleUrlHandlerMapping.setUrlMap(urlMap);
        
        return simpleUrlHandlerMapping;
    }

    @Bean
    public WelcomeController welcome() {
        return new WelcomeController();
    }
}
  • XML 환경설정에서도 아래와 같이 똑같이 설정할 수 있다.
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <value>
            /simpleUrlWelcome=welcome
            /*/simpleUrlWelcome=welcome
        </value>
    </property>
</bean>
<bean id="welcome" class="com.baeldung.WelcomeController" />
  • XML 환경설정을 다음과 같은 것들이 중요하다. java.util.Properties 클래스에 의해 받아지는 폼에서 < value > 태그 사이의 매핑이 이루어진다.

  • 매핑을 할 때는 path = Handler_Bean_Name 의 규칙을 따른다.

  • URL이 보통 리딩 슬래시와 함께 작성되지만, 경로가 하나로 시작하는 게 아니라면 스프링 MVC는 자동으로 추가해준다.

  • XML에 위의 예를 설정하는 다른 방법은 'props' 속성을 'value' 속성을 대신해 사용하는 것이다.

  • Props은 prop태그의 리스트를 가진다.

  • prop태그를 통해서 매핑하는데, 'key'가 매핑된 URL을 참조하고 태그의 값이 빈의 이름이다.

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="/simpleUrlWelcome">welcome</prop>
            <prop key="/*/simpleUrlWelcome">welcome</prop>
        </props>
    </property>
</bean>
  • 아래 테스트 코드가 "/simpleUrlWelcome" 요청이 'welcome' 뷰를 반환하는 'WelcomeController'에 의해 핸들되는지 확인한다.
public class SimpleUrlMappingConfigTest {
    // ...

    @Test
    public void whenSimpleUrlMapping_thenMappedOK() {
        mockMvc.perform(get("/simpleUrlWelcome"))
          .andExpect(status().isOk())
          .andExpect(view().name("welcome"));
    }
}

ControllerClassNameHandlerMapping

  • URL과 일치하는 클래스 이름을 갖는 빈을 컨트롤러로 사용한다.

  • ControllerClassNameHandlerMapping은 URL을 등록된 컨드롤러 빈(@Controller 주석이 언급된 컨트롤러)에 매핑한다.

  • 컨트롤러의 이름은 같은 이름을 가지거나 그 이름으로 시작한다.

  • 많은 경우에서 더 편리할 수 있는데, 특히 하나의 요청 형을 핸들링하는 단순한 컨트롤러 구현체에 경우 더욱 편리하다.

  • 스프링 MVC에서 사용하는 규칙은 이름에서 Controller를 제외하고 접미사를 소문자로 "/"을 붙여서 매핑한다.

  • "WelcomeController"는 "/welcome*"을 매핑한다. 즉, "welcome"으로 시작하는 어떠한 URL이 매핑된다.

@Configuration
public class ControllerClassNameHandlerMappingConfig {

    @Bean
    public ControllerClassNameHandlerMapping controllerClassNameHandlerMapping() {
        return new ControllerClassNameHandlerMapping();
    }

    @Bean
    public WelcomeController welcome() {
        return new WelcomeController();
    }
}
  • ControllerClassNameHandlerMapping이 스프링 4.3에서 사라지게되었다. 주석 주도 핸들러 방법이 선호되어지기 때문이다.
  • 컨트롤러 이름이 항상 컨트롤러 접미사를 지우고 소문자로 반환한다는 것도 중요하다.
  • 따라서 '/welcomTaehwan'은 요청에서 처리하지 못하고 '/welcometaehwan'의 요청만 처리할 수 있다.
  • 자바와 아래의 XML 환경설정 모두에서, ControllerClassNameHandlerMapping 빈을 정의하고 요청을 처리할 때 사용할 컨트롤러에 빈을 등록한다.
  • '/welcome'으로 시작하는 요청을 핸들링하는 "WelcomeControlle"를 XML로 만들어보자.
@Configuration
public class ControllerClassNameHandlerMappingConfig {

    @Bean
    public ControllerClassNameHandlerMapping controllerClassNameHandlerMapping() {
        return new ControllerClassNameHandlerMapping();
    }

    @Bean
    public WelcomeController welcome() {
        return new WelcomeController();
    }
}
  • 위의 XML 환경설정을 사용하면, '/welcome'에 대한 요청이 "WelcomeController"에 의해서 핸들링된다.
  • 아래 코드는 "welcometest"과 같은 "/welcome*" 요청이 'WelcomeController'에 의해서 핸들링이 되는지 테스트하고 welcome' 이름의 뷰를 반환하는지 확인한다.
public class ControllerClassNameHandlerMappingTest {
    // ...

    @Test
    public void whenControllerClassNameMapping_thenMappedOK() {
        mockMvc.perform(get("/welcometest"))
          .andExpect(status().isOk())
          .andExpect(view().name("welcome"));
    }
}

DefaultAnnotationHandlerMapping

  • 어노테이션으로 URL과 컨트롤러를 매핑한다.
  • 들어오는 HTTP 요청을 주석이 있는 컨트롤러 중에 적절한 것으로 보내준다.
  • 요청이 들어오면, DefaultAnnotationHandlerMapping이 스프링 ApplicationContext에 등록된 모든 빈을 스캔해서 '@RequestMapping', '@GetMapping', '@PostMapping'이나 비슷한 어노테이션을 갖는 빈을 모두 찾는다.
  • 그리고 이 정보를 활용해서 어떤 컨트롤러가 요청을 핸들링할 지 결정한다.
  • 또한 DefaultAnnotationHandlerMapping은 매핑 처리의 동작을 커스터마이징하는 많은 어노테이션도 지원한다. (e.g. '@PathVariable', '@RequestParam', '@RequestBody')
  • 기본설정으로, DefaultAnnotationHandlerMapping이 스프링 MVC 구성에 포함되어 들어오는 HTTP 요을 핸들링하는데 사용된다.
  • 하지만 필요하다면, 다른 매핑 전략으로 대체되거나 커스터마이징 될 수 있다.

Refference

· 약 11분
이태환
  • 백엔드를 배워본 적이 없는 학생이 김영한 스프링 부트 입문 강의를 듣고 작성한 글이다.
  • 스프링 부트를 통해서 손쉽게 스프링 프레임워크를 활용한 웹 어플리케이션 서버를 만든다.
  • 클라이언트 사이드인 웹 브라우저가 보내는 요청을 내장 톰캣 서버를 통해 스프링 컨테이너에서 처리하여 반환 시킨다.

스프링 부트 구성요소 톺아보기

내장 톰캣 서버

  • 아파치 소프트웨어 재단에서 만든 WAS, 웹 어플리케이션이다.
  • 자바 서블릿과 JSP, JavaServer Pages를 실행할 수 있는 환경을 제공한다.
  • Jakarta Servlet, Jakata Server Pages, Jakarta Expression Language 등의 사양을 구현한 오픈소스 소프트웨어이다.

아파치와 톰캣의 차이점

  • 아파치 소프트웨어 재단의 아파치 웹 서버이다.
  • 정적 데이터인 HTML, CSS, 이미지 등을 처리하는데 빠르지만
  • 톰캣은 WAS로서 JSP, 서블릿 처리, DB 연결 및 데이터 조작 등의 동적인 데이터를 처리하는데 적합하다.

정적 / 동적 데이터

정적 데이터

  • 사용자의 요청에 따라 변하지 않는 데이터이다.
  • HTML, CSS, 이미지 파일과 같은 데이터를 말하며, 정적인 데이터는 웹 서버를 통해서 처리된다.

동적 데이터

  • 사용자의 요청에 따라 변하는 데이터를 의미한다.
  • 사용자가 입력한 정보를 바탕으로 DB에서 정보를 검색하거나 조작하여 반환되는 데이터들을 말한다.
  • 이러한 동적 데이터는 WAS에서 처리된다.

웹 서버와 웹 어플리케이션 서버, WAS와의 차이점

스프링 컨테이너와 빈

  • 스프링에서, 어플리케이션의 중추를 구성하는 객체와 스프링 Ioc 컨테이너에서 관리되는 객체들을 빈이라고 한다.
  • 빈은 간단히 구체화된 객체로 스프링 IoC 컨테이너에 의해 조립되거나 관리되어진다.
  • 이런 빈과 그것들 간의 의존관계가 컨테이너에서 사용하는 Configuration 메타데이터에 반영된다.

컨테이너

BeanFactory

  • org.springframework.beans.factory.BeanFactory는 스프링 IoC 컨테이너의 실체의 대표이다.
  • 스프링 IoC 컨테이너는 앞서 언급했던 빈들을 담거나 관리하는 책임이 있다.
  • BeanFractory 인터페이스는 스프링의 중심 IoC 컨테이너 인터페이스이다.
  • 스프링으로 바로 제공되어지는 BeanFactory 인터페이스의다양한 구현들이 있다.
  • 가장 많이 쓰이는 구현이 XmlBeanFactory 클래스이다.
  • 이 구현은 우리가 앱을 구성하는 객체들과 의심할 여지없이 많은 객채들 간의 상호 의존관계를 XML의 언어로 표현하도록 한다.
  • XmlBeanFactory는 XML Configuration 메타데이터를 취해서 완전하게 환경설정된 시스템이나 앱을 만드는데 사용한다.

4.2 Basics - containers and beans

ApplicationContext

  • ApplicationContext 인터페이스도 스프링 IoC 컨테이너를 대표하고 구현, 환경설정, 빈을 조합하는 역할을 수행한다.
  • BeanFactory의 대체 인터페이스로 더 많은 기업에 특화된 기능들을 제공한다.
  • 제공되는 기능으로 메시지 확인(다국언어 지원), 이벤트 게시, 어플리케이션 레이어 특화 문맥이 있다.
  • 어플리케이션 컴포넌트에 접근할 때 사용하는 ListableBeanFactory 메서드를 상속 받았고
  • 일반적인 방법으로 파일 리스소를 읽는 기능을 ResourceLoader로부터 상속 받았다.
  • ApplicationEventPublisher로부터 상속받은 등록된 리스너들의 이벤트를 게시하는 능력도 가지고 있다.

컨트롤러란 무엇인가?

  • 전형적인 MVC, Model-View-Controller 아키텍처에서 Controller의 개념을 들여다보자.

컨트롤러의 역할

  • 들어오는 응답을 가로챈다.
  • 응답의 내용을 데이터 내부 체계로 변환시킨다.
  • 추가의 처리를 위한 모델에 데이터를 보낸다.
  • 모델로부터 처리된 데이터를 얻어와 렌더링을 위해 해당 데이터를 뷰로 전달한다.

스프링 MVC 다이어그램

  • 여기서 발송 서블릿이 아키텍처에서 Front 컨트롤러의 역할을 수행한다.
  • 위 MCV 다이어그램이 전형적인 MCV 컨트롤러 뿐만아니라 RESTful 컨트롤러에 해당된다. (약간만 다르다.)
  • 전통적인 접근법으로 MVC 어플리케이션은 서비스 지향이 아니라서 뷰 리졸버를 통해 컨트롤러로부터 얻어낸 데이터를 기반으로 최종 뷰를 렌더링한다.
  • RESTful 어플리케이션은 서비스 지향으로 디자인되어서 원본 데이터(주로 JSON)을 반환한다.
  • 이러한 어플리케이션이 어떠한 뷰 렌더링도 하지 않기 때문에, 뷰 리졸버가 존재하지 않는다.
  • 그래서 컨트롤러가 일반적으로 직접 HTTP 응답을 통해 데이터를 전송할 것으로 기대한다. 출처

Maven Dependencies

  • Maven은 빌드 자동화 도구로 주로 자바 프로젝트에서 쓰인다.
  • 스프링에서는 Maven이 의존성들을 구성하고 프로젝트를 빌드하는데 사용된다.
  • 다음은 스프링 부트에서 스프링 MVC로 동작하기 위해서 Maven 의존성을 다뤄본다.
<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.0.2</version>
</dependency>

스프링 부트 Web Config

  • 스프링 부트에서 어떻게 구성하는지 들여다보자.
  • 클래서 경로에 thymeleaf 의존성을 추가했기 때문에, 이를 위한 어떤 @Beans도 구성할 필요가 없습니다.
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  • WebConfig에서 기본 설정 서블릿과 Greeting 객체와 오브젝트 매퍼를 위한 빈을 가능하도록 추가해준다.
@Bean
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> enableDefaultServlet(){
	return factory -> factory.setRegisterDefaultServlet(true);
}

@Bean
public Greeting greeting() {
	Greeting greeting = new Greeting();
	greeting.setMessage("Hello World !!");
	return greeting;
}

@Bean
public ObjectMapper objectMapper() {
return new ObejctMapper();
}
  • 예를들어, 만약 컨트롤러가 "welcome" 이름의 뷰를 반환하면, 뷰 리졸버는 템플릿 폴더 안에 "welcome.html"이라 불리는 페이지를 처리한다.
  • 템플릿 폴더가 thymeleaf가 뷰를 찾도록 기본 설정된 폴더이다.

MVC 컨트롤러

  • MVC 스타일의 컨트롤러를 구현하자
  • 어떻게 ModelAndView 객체를 반환하는지 주목하자.
  • ModelAndView 객체는 모델 맵과 뷰 객체를 포함하고 둘다 뷰 리졸버에서 데이터 렌더링에 사용된다.
@Controller
@RequestMapping(value= "/test")
public class TestController {
	@GetMapping
	public ModelAndView getTestData() {
		ModelAndView mv = new ModelAndView();
		mv.setViewName("welcome");
		mv.getModel().put("data", "Welcome Home man");
		return mv;
	}
}
  • 여기까지 정확하게 구축한게 여기있다.

  • 먼저 TestController라고 불리는 컨테이너를 만들고 그것을 "/test" 경로에 매핑했다.

  • 클래스에서, ModelAndView 객체를 반환하는 메서드를 만들고

  • GET 요청을 통해서 메서드를 패밍하여 "test"로 끝나는 URL 호출이 발송 서블릿으로 라우팅되어 TestContoller의 getTestData 메서드로 전달됩니다.

  • 물론 ModelAndView 객체에 추가로 몇몇 모델 데이터도 반환한다.

  • 뷰 객체는 "welcome"으로 설정되었고 위에서 언급한대로, 뷰 리졸버가 템플릿 폴더 안에 'welcome.html'으로 불리는 페이지를 찾는다.

REST 컨트롤러

  • 스프링 RESTful 어플리케이션 구축은 MVC어플리케이션의 것과 뷰리졸버와 모델 맵이 없다는 것 빼고는 같다.
  • API가 간단히 JSON과 같은 방식의 원본 데이터로 클라이언트에게 반환한다.
  • 그래서 발송 서블릿이 뷰 리졸버를 우회하여 HTTP 응답 바디의 데이터를 바로 반환한다.
  • 간단한 RESTful controller의 구현을 보자.
@RestContorller
public class Restcontroller {
	@GetMapping(value = "/student/{studentId}")
	public Student getTestData(@PathVariable Integer studentId) {
		Student student = new Student();
		student.setName("Peter");
		student.setId(student.ID);
		return student;
	}
}