웹 개발 Spring Day 1 프로젝트 생성, MVC패턴, 웹 데이터의 흐름

김지원·2022년 7월 22일
0

WebDevelop2

목록 보기
20/34

프로젝트 생성

1. 'Spring Initializr' 선택

2.

Name : 프로젝트 이름 (실제 역할 없음)
Location : 프로젝트 저장 위치 ( ~ : 내문서 )
Language : 개발 언어
- c언어는 java와 남이다. 
- java가 낳은게 Kotlin이다.Kotlin을 컴파일하면 java가 된다.
Type : 의존성 관리자(Dependency Manager)의 종류 (Maven) Gradle도 같다.
Group : 프로젝트 소유자 도메인의 역순 
Artifact : 찐 프로젝트 이름 (사람들에게 공개되는 이름) 
- 대부분의 경우에는 Name과 Artifact을 똑같이 설정해준다.
Package name :  베이스 패키지(Base Package) 경로. 
- 베이스 페이지 구성 -> (`Group` + `Artifact`)
  절대로 베이스 패키지보다 상위에서 코드를 작성하지 않는다.
JDK : 어쩌고 1.8
Java : 8
Packaging : `War` -> 웹 아카이브.
Jar처럼 실행파일인데 다만 안에 웹 전용으로 개발된 내용이 있다면 war을 사용한다.
Jar : 자바로 작성한 내용들이 1차적으로 컴파일하면 나오는  

1.8 == 8 인데 Java : 8 얘를 왜 사용할까
8은 코드가 돌아갈 버전은 아니다.
버전이 올라가면 올라갈수록 새로운 기능이 들어오게 된다.
그런 문법적인 것 기능들을 몇 버전 기준으로 사용할것인지에 대한 기준이다. IDE의 기준이다.
코드가 작성은 되지만 실행이 안됨 17은! 두개를 맞춰주면 된다.

type에 대해서..

JSON
JSON Array [...] 
JSON Object {k : v, k1:v1, k2:v2, k3:v3}
JSON Object 포맷(형식)은 대부분의 언어에서 널리 사용이 되는데 우리가 직접 만들지 않는다. 힘들다.
누가 만든 것을 갖다쓰려면 
의존성관리자 때문에 괜찮다. 녹음. 

배열인 것처럼 보이는 문자열의 인덱스에 접근하여 그 값을 뽑아내고 싶다면? 이러한 로직을 짜는 것은 굉장히 힘들 것이다. 그래서 우리는 JSON이라는 의존성을 사용하기로 한다.
String s = "[1, 5, 7, 11, 13, 17]";
? ja = ?.?(s);
System.out.println(ja.get(5));
JSON Array […]
JSON Object {k : v, k1 : v1, k2 : v2, k3 : v3}

String s = "[1,3,5]";
JSONArray ja = new JSONArray(s);
System.out.println(s.toString());


3. Spring boot : 2.6.x

  • Spring boot 의 전역적인 버전을 뭘 사용할건지에 대해 설정한다.

4. 아래 의존성 선택
포로젝트 생성단계에서 자동으로 집어넣을 수 있다.

Spring Boot DevTools : 개발을 용이하게 해주는 의존성
Spring Web : 웹과 관련된 의존성. 
Thymeleaf : 스프링 부트와 HTML 파일 간의 정보 전달 및 HTML 개발을 용이하게 하기위한 의존성

  • 프로젝트 생성 시 젤 처음에 뜨는 창이다.

프로젝트 구조

.ieda : IDE의 프로젝트에 대한 설정
.mvn : Maven 관련 설정/실행 파일 포함
* src * : Java, HTML, CSS , Javascript, 서버 설정 등
.gitigore : VCS(Version Control System) 제외 파일 설정 
HELP.md : 삭제
{프로젝트 이름}.iml : IDE 설정
mvnw : Maven 실행 파일 (리눅스 / 맥 전용)
mvnw.cmd : Maven 실행 파일 (윈도우 전용)
* pom.xml * : 프로젝트 걸정 및 의존성 관리용 파일

. 으로 시작하는애들은 숨김이라는 의미이다. (윈도우에서는 .으로 시작하는 애들이 없기 때문에 보이게 된다.)

  • 참고로 mac은 보이지 않는다.

pom.xml

1. 구조

project > parent > version : 해당 프로젝트에 추가 되어있는 의존성 중
groupId 값이 'org.springframework.boot'인 의존성들의 버전을 전역적으로 지정하는 값

  • org.springframework.boot가 붙은 의존성의 버전들을 위에서 설정한다.

project > groupId : 프로젝트 생성시 'Group' 값 
project > artifactId : 프로젝트 생성시 'Artifact' 값
project > version : 프로젝트 버전 
project > packaging : 프로젝트 압충 방식 (jar 혹은 war)
project > name : 프로젝트 생성시 'Name' 값
project > description : 프로젝트 설명 
project > properties > java.version : 프로젝트 생성시 'Java' 버전 

project > dependencies : 의존성 정보를 담음,

  • 여기서 얘가 붉은색이면 위의 parent태그의 버전을 복붙해서 집어넣고 구동을 시키고 지우고 구동을 시키면 된다. 붉은색이 되는 이유는 재수가 없어서...

2. 공통

  • 조금이라도 변경 사항이 생기면 나타나는 `Load Maven Changes 버튼을 반드시 클릭해야 변경사항이 적용된다. (화면 우측 상단)

src

1. 구조

main > java* : 여기부터 Java 코드로 작성한다. 
혹시, 폴더가 파란색이 아니라 그냥 여느 폴더와 마찬가지의 색이라면 
프로젝트 구조가 잘못된 것이다. 
main > java > {base package} > {프로젝트 이름}Application : 스프링부트 진입점

main > resources > static* : 정적인 리소스를 담는다. 
가령 정적인 HTML파일, CSS, JS, 이미지, 영상 파일 등.
맵핑(Mapping)된 주소는 루트(/) dlek.
main > resources > templates* : 
컨트롤러(Controller) 및 기타 뷰(View)와 연결될 구성요소의 동적인 내용을 담을 HTML 파일을 담는다.
즉, Thymeleaf 템플릿 엔진이 적용될 HTMl 파일이 위치하며, 해당 폴더에 파일을 위치시키는 것으로는 맵핑되지 않는다. 
main > resources > application,properties : 서버 설정 파일 
test* : Java 단위 테스트를 폴더

MVC(Model-View-Controller)

  1. HTTP 프로토콜위에서 클라이언트(Client)의 요청 (Request)에 대한 서버(Server)의 응답(Response)을 만드는 것이 웹 개발이다.
요청을 발생시키는 방식
주소창에 `naver.com` 을 치는 것이 요청이다.
응답은 우리가 보고있는 뉴스, 광고와 같은 화면이다. 

우리가 무의식중에 '주소'라는 것을 많이 사용한다.
https://section.cafe.naver.com/ca-fe/

  1. 사용자의 요청을 받을 수 있도록 특정 URL에 대해 요청을 받을 수 있도록 하는 것을 맵핑이라 하고, 이러한 맵핑은 메서드(Method)이며 이러한 맵핑 메서드를 포함하는 클래스(Class)를 컨트롤러(Controller)라 한다.

http://내도메인/hello

  • http://localhost:8080/hello 이 주소로 맵핑이 없어서 표시할게 없다는 뜻이다.
  • type=Not Found, status=404 오류 발생
  • 맵핑이 있어야 원하는 페이지가 뜨게 되고 없으면 404오류가 발생한다.
  • 해당 맵핑에 맞게 들어오면 메서드를 실행한다.

localhost : 내가 쓰는 컴퓨터 (자기자신)
8080 : 기본값.

  1. 뷰 (View)
    사용자에게 표시될 화면을 의미하는데 좁은 의미에서 Thymeleaf나 기타 템플릿 엔진 및 이와 관련된 HTML 파일을 의미한다. (templates에 있는 파일만 view 이다.)

  2. 모델 (Model)
    컨트롤러가 받은 요청에 대해 값을 검증하고 로직을 가지는 역할인 서비스(Service)와 서비스로 부터 넘어온 검증된 데이터를 입출력(CRUD)하는 리포지토리(Repository, DAO : Data Access Object, Mapper)로 이루어져있다.
    스프링부트 프로젝트에서 유일하게 데이터베이스(DBMS)와 통신할 수 있는 객체는 리포지토리이다.


3개의 구성요소로 웹개발을 진행하는데

요청은 Controller가 받게 된다.
요청에 email, password가 담겨서 오고 이 두개가 맞는지 Model의 Service가 확인한다.
컨트롤러가 모델의 서비스에 이 값들을 넘겨준다. 회원정보는 DB에 저장이 되어있다. 리포지토리만 접근을 할 수 있다. 서비스가 e/p를 전달해서 리포지토리가 DB에서 확인을 하려면 Read : SELECT를 해야한다.
그러면 DB가 SELECT 결과를 리포지토리에 주고 서비스에게 전달되고
서비스는 컨트롤러에게 알려준다.
화면에도 보여줘야하기때문에 view에게 보내준다.
html로 비밀번호가 맞는지 아닌지 다시 컨트롤러에게 주고 컨트롤러가 응답을 보내준다.

라이브러리 : 주소찾기나 차트
전반적으로 영향을 미치는 것 프레임워크

tomcat
자바가 구동되는 환경 JVM 위에 tomcat이 돌아가고
그 위에 springFrameword 그 위에
Java Servlet(JSP) -> 우리가 만든 웹서버가 돌아간다.

RestController의 맵핑들을 싹 모아놓은 것이 API이다. => 웹에서는


Controller ← → Service ← → Repository ← → DB

MVC 패턴

: 웹 개발을 하기위한 통일된 규격


M(Model)

: 서비스(Service)와 저장소(Repository)를 가진다.

서비스(Service)

: 모델에 속하며, 로직을 담고있고, 컨트롤러(Controller)와 저장소(Repository)의 이어주는 역할을 한다.

  • 컨트롤러가 직접적으로 저장소에게 접근을 하지 못한다. (작동은 하지만 좋은 개발은 아니다.)
  • 컨트롤러, 서비스, 저장소는 클래스로 이루어져있다. (저장소는 어떻게 로직을 짜느냐에따라 다르다.)

저장소(Repository)

: 모델에 속하며, Spring Boot에서 작성하는 구성 요소 중에 유일하게 데이터베이스(DBMS)에 직접 접근할 수 있는 객체다.


V(View)

: 클라이언트(Client)에게 보여질 화면을 구상한다. (HTML, Thymeleaf)

  • 서버단에서 컴파일 되어서 클라이언트에게 보여줄 때는 <a thLtext="${1+1}"></a>(Thymeleaf사용) 이와 같은 코드가 아닌 <a>2</a>(브라우저에서 소스보기 했을 때처럼) 으로 보여주어야한다.
    thymeleaf로 적혀있는 html파일을 동적인 html파일이라고 한다. 지금까지의 html, js, css는 정적인 컨텐츠라고 한다.

C(Controller)

: 서로 연관있는 맵핑 메서드(Mapping Method)의 집합이다.

맵핑 메서드(Mapping Method)

: 컨트롤러가 포함하고 있으며, 클라이언트가 서버(Server)로 요청할 수 있는 주소이고 이에 대한 실행 단위이다. (지정된 주소로 접속시 특정 메서드를 실행할 수 있도록 함)

엔티티(Entity)

: 데이터베이스 테이블이 가진 열(Column)들과 1:1로 매칭되는 멤버 변수 및 이에 대한 Getter(+Setter) 메서드를 가지는 것. ( 이외에 것을 가지면 안된다. (equals 제외) 선택적으로 equals로 재정의해도 괜찮다.)

값 객체(Vo, Value Object), 데이터 전송 객체(DTO, Data Transfer Object)

: 엔티티를 상속받아 추가적이 기능을 구현하기 위해 사용하는 것 혹은 테이블과 관계없는 테이터를 주고 받기 위해 사용한다.

  • 일종의 Controller는 창구라고 생각하면 쉽다.

/user/login 이라는 주소를 치고 들어갔다면 로그인할 수 있는 페이지가 뜰 것이다. 만약 회원가입 페이지나, 회원정보 수정 페이지가 뜬다면 맵핑을 잘못한 것이며 올바른 메서드가 실행된 것이 아니다.

엔티티(Entity)는 없어도 안되고 내용이 더 있어도 안되는데 더 있는 경우가 있을 때에 값객체나 데이터 전송 객체를 만드는데 엔티티를 상속받아서 추가 기능을 구현하면 된다.


웹 개발 데이터의 흐름

  1. 태초에 웹 서버가 돌아가고 있었다.
  1. [ 컨트롤러 ] 특정 주소에 대한 요청(Request)을 컨트롤러의 맵핑 메서드가 받는다.
    (가령 로그인, 이 때 컨트롤러는 로그인에 대한 정보(이메일, 비밀번호 등)에 대한 로직 처리나 데이터베이스 조회를 절대 직접하지 않고 서비스에게 넘긴다.)
  1. [ 컨트롤러 ] <2>에서 필요한 로직을 처리하기 우해 서비스에 데이터를 넘긴다.
  1. [ 서비스 ] 컨트롤러가 넘겨준 데이터를 정규화(Normalization)한다.
    (정규화 : 어떠한 데이터가 주어진 정규식(Regular Expression)에 부합하는가에 대한 여부를 확인하는 행위. 어떠한 데이터가 우리가 정한 규격에 맞는지 확인하는 것.
    가령 비밀번호가 영문 소문자, 대문자, 숫자, 특수기호 등으로 이루어진 8자 이상 16자 이하 인지 확인하는 그러한 것)
  1. [ 서비스 ] 정규화된 데이터를 필요하다면 데이터베이스 사용을 위해 저장소에 넘긴다. (받은 이메일과 비밀번호가 일치하는 레코드를 가져와야함. 단, 서비스는 절대로 데이터베이스에 직접 접근하지 않는다.)
  1. [ 저장소 ] 서비스로부터 넘겨받은 데이터를 그 요구에 맞게 데이터베이스에서 작업한다. (서비스에게 받은 이메일과 비밀번호가 일치하는 레코드를 서비스에게 돌려준다. 없으면 null 돌려줌.) / (서비스가 CRUD 중 어떤걸 할지 정해주게 된다.)
    데이터의 흐름으로 보면 저장소는 대부분 반환점이다.
  1. [ 서비스 ] 저장소로부터 처리된 결과를 돌려 받아 컨트롤러에게 돌려줄 결과를 돌려준다. (저장소로부터 받은 회원 정보가 포함된 레코드가 있으면 이메일 / 비밀번호가 옳은 것, 없으면(null 이면) 이메일 혹은 비밀번호가 틀렸다는 뜻.
  1. [ 컨트롤러 ] 서비스로 부터 받은 결과에 대한 적절한 응답(View 포함, Response)을 만들어 클라이언트에게 돌려준다. (서비스로부터 받은 로그인 결과를 클라이언트에게 돌려준다. ) ( 적절한 응답에는 view가 있을 수도 없을 수도 있다.)
  1. 다시 서버는 돈다. (Thread가 도는 것)

회원가입 성공실패여부를 반환한다고 해보자.

-> 컨트롤러에서 서비스가 가진 register라는 메서드를 호출할 것인데 데이터를 전달할 수 있는 방법이 있나?

  • ( ) 에 데이터를 전달할 방법이 하나도 없다.
    그래서 ( )에 String name, int age, Date birth, String email ... 와 같은 것들을 다 작성해야한다. 너무나 하드 코딩이기때문에 다 작성할 수 없다.

그래서 값 객체(Value Object) 라는 것을 만들어줘야한다.

회원정보를 가진 users 테이블이 있을 것이다. 해당 테이블의 열들과 register() 괄호에 들어가는 것들이 1:1로 매칭이 되는 entity를 만들게 된다.

-> userEntity라는 클래스를 생성하고 열과 1:1매칭으로 멤버변수가 만들어져야한다.

  • 그런데 멤버변수들의 접근제한자가 Private으로 만들어졌기 때문에
    UserEntity userEntity = new UserEntity를 해서 어디서도 사용할 수 가 없다.

-> 그렇게 때문에 getter, setter 메서드를 만들어서 사용을 한다.
ex) getIndex : index값을 받아올 수 있는 것.
setIndex : index값을 지정할 수 있는 것.

가지고 있는 값을 개발자가 마음대로 바꿀 수 없다. 그래서 setter에 특정 로직을 나두게 된다.

저장소가 회원정보를 뱉어내는데 하나하나를 따로 줄 수 없기 때문에( ()의 값들을 다 뱉어내는 것 ) 반환타입이 UserEntity이다. userEntity.getEmail() 과 같은 느낌으로.


DB에서 index는 1부터 시작이 된다.
신입이 setIndex에 -1을 하려고 시도를 했다면 막아야한다. (받은 index가 -1이면 안된다.)

  • UserEntity setIndex에 -1을 했을 때 예외처리를 해주었다.
    그리고 main에서 setIndex에 -1값을 넣어보면 어떻게 될까?

  • userEntity 빨간줄이 그어있다.
  • userEntity.setIndex는 예외를 던질 수 있다 라는 메서드 자체의 기능대로 있어서 그렇다.
    setIndex 라는 메서드를 당겨서 사용하려면 이 메서드를 호출하고 있는 메서드도 그 예외를 던질 수 있다라고 처리를 해주거나 try catch를 통해 예외를 처리한다.

-> 1. 호출하고 있는 메서드에도 예외를 던진다는 처리 해준다.

-> 2. try-catch로 예외 처리

  • 그러면 ㅗ가 출력이 될것이다.

어떤 예외가 터졌는지 보고 어떤 것을 빼먹엇는지 실수를 했는지 찾을 수 있다.
NullPointException 가 뜨면 뭔가가 Null값이다 라는 것을 한번에 알 수있다.
그냥 Exception을 던지지 않고 나만의 Exception을 만들어야한다.

InvalidValueException와 같은 예외 클래스를 만들어야한다.
이름만 그렇지 그냥 클래스이다.

throws new InvalidValueException : throwable이 아니다라고 뜬다.
예외를 상속받으면 된다. ( extends Excption을 해줘야한다. )
예외를 축소시켜야한다.

작동시키면 Exception이 아니라 InvalidValueException이 뜨게 되고
그렇게 되면 값이 잘못됬다고 이해를 할 수 있다.

InvalidValueException에는 기본 생성자밖에 없기 때문에 메세지를 남길 수 없다.
생성자가 딱 하나 있는데 그 하나 있는 생성자가 매개변수가 없기 때문에 그 매개변수에 맞는 전달인자를 전달해줄 수 없다.
그래서 Exception이라는 부모클래스가 가진 생성자를 그대로 다 가져오면 된다.

그렇기 때문에 세터가 존재해야한다!!!


InvalidValueException으로 예외를 처리하게 되면 InvalidValueException으로 오류가 발샏한 것을 알 수 있게 된다.

encapsulation

멤버는 private로 잘 막아놓고 setter로 접근하게 해야한다.
이런걸 캡슐화 한다고 말한다.


entity equals

  • 참조타입의 객체를 비교할 때 이렇게 절대로 사용하지 않는다.
  • 객체를 여기저기 쓰다보면 내가 가지고 있는 객체가 다시 돌아온 객체와 원래 같은 객체인지 비교할 때 가끔씩 쓰기도 한다.
  • 레퍼런스 타입이 동일한가에 대해서는 equlas로 사용한다.
    sout(user1.equals(user2));

sout(user1.equals(user2)); // false
object클래스로 연결이 된다. object를 상속하고 있고
sout("a".equals("a")) string으로 연결된다.

sout(user1.equals("kkkk"));

equlas

UserEntity user  = (UserEntity) obj;
return user.getEmail().equlas(this.getEmail());

Entity에는 테이블의 열과 매칭되는 1:1 멤버변수 그리고 equals 오버라이드 말고는 더 있으면 안된다.
그런데 더 있어야하는 경우가 생긴다. 그럴 경우에는 값 객체와 데이터 전송 객체를 만드는데 해당 Entity를 상속받아서 추가 기능을 구현하면 된다.
RegisterVo 생성
회원가입할 때 UserEntity가 가진 모든 멤버변수들이 다 필요하다.
다 복사해서 적으면 비효율적인 코딩이 되버리니깐 UserEntity를 상속 받자.
UserEntity 기능을 RegisterVo 객체를 통해 그대로 사용이 가능하다.

UserEntity에 RegisterVo 객체화해줘야한다.

RegisterVo registerVo = new ResigterVo

sout( user1.equals(registerVo) ); // true
UserEntity는 부모고 RegisterVo는 자식이다.
=> 부모타입은 자식객체를 받을 수 있기 때문에 true가 뜬다.
user1에 equals을 호출하면 애초에 object이기때문에 들어가는 것에는 문제가 없고, UserEntity 타입으로의 instanceof 형변환이 가능한지에 대해 물어본다. 변하는 대상은 자식객체이기때문에 형변환이 가능하다.

그리고 RegisterVo에는 회원가입 결과를 담을 수 있다.
SUCCESS, EMAIL_DUPLICTE... 과 같은 것들을 열겨형으로 만든다. (enum)

profile
Software Developer : -)

0개의 댓글