Spring/스프링 프로젝트

스프링 프로젝트 개발순서

어굴애 2021. 7. 9. 10:12

내가 이해한 바를 바탕으로 스프링 프로젝트 개발의 전반적인 흐름을 정리했다.

이제 개발을 배우고 있는 입장이기 때문에 부정확한 내용이 많을수도 있지만,

강의 조금 듣고 프로젝트를 시작하니 어디서부터 어떻게 시작해야할지 몰라 막막했기 때문에 혹시라도 그런 사람들에게 참고가 되라고 작성한 글이다.

단 빈을 주입하는 부분이나 어노테이션, 각각의 컴포넌트가 어떻게 연결되는지에 대한 설명은 다루지 않았다.

나중에 수정될 예정

 

 


데이터를 넘겨주는 방식

1. QueryString : 간단한 문자열과 주소창에 노출돼도 상관없는 정보 Get
2. Form : 일반적으로 가장 많이 사용되는 방식 Post가 기본

기타 : Session, Cookie

          [꼭 뷰단에서 설정해주는 내용은 아님, 뷰단에서 하지 않는 게 바람직하다]

 

QueryString

  • Get방식으로 처리한다.
  • url주소 뒤에 ?key1=value1 & [key2=value2] 이렇게 주소로 전달하는 간단한 값들을 쿼리스트링이라고 한다.
  • <a href="/url구구절절?team_code = ${member.team_code}> 이때 ? 뒤에 오는 값들이 쿼리스트링

localhost:8090/admin/vacManage/current?emp_id="3734"&team_name="47373"

@GetMapping
public String vcCurrList(Model model, String emp_id, String team_name){

      - emp_id, team_name을 form(Post)으로 넘겨준 값처럼 파라미터에 넣어서 바로 빼서 변수처럼 쓸 수 있다. }

 


스프링의 GET요청과 POST요청

스프링의 요청을 처리하는 방식을 두가지로 나뉜다.

사용자가 특정 페이지를 요청하면 그 페이지에 들어갈 DB를 가져와 화면에 뿌려주는 GET요청,

FORM(대표적인 OST방식)을 입력하여 입력값을 서버로 넘겨주면 그 값을 처리에 DB와 작업하는 POST요청이다.

GET요청은 주로 SELECT 쿼리문을 사용하여 데이터를 읽어오고,

POST요청은 입력받은 값을 가공하여 INSERT, UPDATE, DELETE 쿼리를 통해 DB에 수정을 가한다.

 

 

스프링 개발 순서 [GET 요청]

  • 역순도 가능, 꼭 정석은 아님
  • ex. 휴가신청페이지

1. View단

뷰단은 전체적인 화면과 요소들만 적어두고, 실제로 값을 불러오는 작업은
Mapper, entity까지 완성했을 때 Controller와 비교하며 작성한다.


EL태그와 JSTL태그 라이브러리를 통해 데이터를 꺼내 사용한다.

EL 태그 : ${}로 model이나 session 혹은 뷰단에서 jstl c:set함수로 정의된 변수를 넣어 사용한다.

 

  • 1) model에 담아준 변수가 List<'Member>혹은 List<'HashMap>일때 : forEach, forTokens문 사용
<tr>
<c:forEach var="member" items="${list}">
     <td>사번 : ${member.emp_id} </td>
     <td>부서 : ${member.emp_dept}</td>
</c:forEach> </tr>

 

  • 2) model에 담아준 변수가 객체(Member) 혹은 HashMap일때
<tr>
      <td>사번 : ${member.emp_id}</td>
</tr>

 

  • 3) model에 담아준 변수가 원시형(int, String ..)일 때
    •  하나의 컬럼, 하나의 행을 가지는 결과집합을 가쟈오기 위해 사용 ex)emp_id값 하나
<td> ${name}님의 총연차 : ${tAnnday} // OOO님의 총연차 :19 </td>

 

2. Controller

기본 매핑

@GetMapping("url")

[ DB 출력 후 PostMapping으로 넘겨줌] : form 입력하는 페이지를 보여주기 위한 Mapping으로 SELECT 쿼리 사용

- 여기서는 보통 Model객체, 경우에 따라 Session객체를 파라미터로 받아 DB에서 값을 불러와서 model에 넣어주는 작업

  1) 필요하면 데이터를 변환하는 작업

  2) 주로 바로 서비스함수 호출

  • model.setAttribute("list", XXservice.get(보통 파라미터 생략. 조건이 있을때 넘겨줌. ex. 특정 팀의 직원들 레코드 list)); 
  • model.setAttribute("member", XXservice.get(emp_id));
  • model.setAttribute("tAnnday", XXservice.get(emp_id));

3) 뷰 찾기 : return "타일즈패턴";

 

forward와 redirect

뷰단으로 제어를 넘겨주는 방식에는 forward, redirect 두 가지가 있다.

  • forward : 데이터를 함께 넘긴다. 스프링 컨트롤러에서 model에 값을 넣어 전달할 때 forward를 사용
    → return값에 해당하는 뷰 연결
  • redirect : 데이터 없이 제어만 넘긴다. 새로운 페이지를 url로 요청하는 것과 마찬가지로, model값이 전달되지 않음.
    → 타일즈를 사용하는 환경에서는 response객체의 sendRedirect("url") 호출

 

컨트롤러 파라미터에 자주 쓰이는 내장객체

  • Model : 파라미터에 Model 객체를 넣으면 디스패처서블릿(스프링 내장)이 컨트롤러를 읽어 수행할 때 메소드 구현부(수행코드)에서 Model객체를 사용할 수 있게 한다.
  • HttpServletResponse : sendRedirect()를 위해 받을 필요 있음
    • 우리는 tiles 라이브러리를 사용하기 때문에 return값에 따라 뷰를 찾아준다.
      단 return값을 통해 뷰를 찾으면 url 생성시 현재 위치를 기준으로 상대적인 url을 완성시켜 문제가 됨
    • 👹현재위치 : localhost:8090/admin/empManagement/organization
           (현재위치는 클래스의 @RequestMapping 기준)
    • 👻 원하는 위치 : localhost:8090/admin/vacReqManagement/all [return "admin.vacRequestMangement.all"]
    • 🤡 실제 생성되는 url : localhost:8090/empManagement/admin/vacRequestManagement/all
             (엉뚱한 url 생성됨)
    • return "redirect:/admin/~~"; 이것도 작동 안함
    • 방안 : 파라미터에서 response 객체 직접 받아다가 url주소로 요청 (타일즈 형식 아닌 url 직접 작성)
      response.SendRedirect("/admin/vacRequestManagement/all")

       sendRedirect을 사용하는 경우 
    • Post요청 : 타일즈를 사용하는 환경에서 PostMapping에서 요청을 처리하고 다른 페이지로 이동시킬 때 sendRedirect
    • Get요청 : 사실상 sendRedirect방식이 거의 사용되지 않음.
      쿼리스트링으로 특정 값을 전달받아 요청을 처리한 뒤 다른 페이지를 요청하려고 하는데,
      제대로 페이지가 안나온다면 시도해보기
GET요청에서 sendRedirect를 쓰는 예시
자신의 휴가요청리스트를 출력하는 페이지에서 취소버튼을 누를 때 a링크를 통해 해당 요청번호가

cancelVacReq?holi_rid="${holi_rid}와 같은 형태로 GET요청으로 연결되는 소스다.
쿼리스트링은 GET요청이기 때문에 컨트롤러에서 @GetMapping("cancelVacReq")로 매핑된 메소드를 찾게 된다.
요청번호를 db로 넘겨준 후 해당 요청을 취소하는 업무 처리 후,
우리가 원하는 페이지는 다시 그 요청이 처리되었을 때의 휴가요청리스트를 보는 것이다.
이때 원하는 요청은 "vacList"(리스트를 보는 것)이지 "cancelVacReq"(특정 요청을 취소하는 것)가 아니다.
이 경우 GetMapping 메소드라 할지라도 return "vacList"는 작동하지 않는다.
vacList를 보기 위한 메소드는 이미 클래스내 다른곳에 정의되어 있을 것이다.
@GetMapping("cancelVacReq")이 아닌 @GetMapping("vacList')를 호출하기 위해
foward가 아닌 redirect방식을 사용하게 된다.

 

  • Session : 현재 우리 프로젝트상, 사번, 권한, 이름만 세션에 저장하고 있다.
    • 사용법 : String emp_id = session.getAttribute("id").toString();
                       [프로젝트 내 어디서든 사용 가능. DB에서 불러올필요X]
  • Principal : 시큐리티 세션객체인데, 첫로그인시 Session 설정을 위해서만 사용됨
  • HttpServletRequest : 스프링이 대체하기 때문에 잘 사용하지 않음

 

3. Service

Service inteface : 리턴타입만 주의해주면 됨.

  • public Member getEmpInfo(emp_id);
  • public List getEmpList();

ServiceImpl

  • @Service 어노테이션
  • Dao 함수 호출이 주된 역할, 다른 코드 거의 들어갈 일 없음
  • return memberDao.getEmpInfo(String emp_id);

 

4. DAO

DAO interface : 마찬가지로 함수호출이 끝, 리턴타입만 주의

  • public Member getEmpInfo(emp_id);
  • public List getEmpList();

 

5. Mapper

    • 매퍼는 mySql에서 먼저 쿼리를 완성해서 동작 확인 후 작성한다.
    • **DAO의 메소드와 비교 필수
      • id="dao의 메소드 이름"
      • resultType ="dao의 리턴타입"이지만, 쿼리의 결과집합을 기본적으로 염두에 두어야한다.
        쿼리의 결과 집합이 하나의 레코드(한 행)이 아니라 여러 행이라면 List로 받아야하는데,
        Mapper파일에서는 List를 기재하지 않고, 바로 List가 담을 객체를 resultType에 넣는다
    • parameterType : 조건절을 줄 때만 사용, ex. emp_id를 통해 특정 레코드를 뽑을 때는 String
    • resultType : select문에서만 결과집합이 존재하기 때문에 resultType을 반드시 지정함
      • Entity(클래스이름), HashMap, 원시형(int, String 등)이 올 수 있음

 1) Entity : 가장 보편적인 방식으로 패키지명까지 정확히 포함해서 작성.
주로 결과집합의 컬럼이 많고 테이블에서 컬럼들을 그대로 가져올 때 사용

<select id="getEmpInfo" resultType="Member" [parameterType="String"]>
     select emp_id, emp_name, emp_level, emp_posi, ... from employee [where ... ]
</select>

2) HashMap : entity에 없는 가상컬럼을 불러올 때 (ex. 총연차-사용연차 as 잔여연차)
혹은 결과집합 컬럼이 몇 없을 때 → 클래스를 직접 만들 필요가 없음

  • 컨트롤러에서 HashMap의 이름을 map으로 주고, 뷰단에서 ${map.emp_name} ${map.emp_dept}으로 사용 가능
<select id="getEmpInfo" resultType="HashMap" [parameterType="String"]>
    select emp_name, org_teamname as emp_dept from employee e join organization o on           
    e.emp_dept=o.org_teamname [where ... ]
</select>

3) 원시형 : 결과집합이 1행 1열의 레코드일 때 [ex.통계값]

<select id="getEmpInfo" resultType="String" parameterType="String">
     select emp_posi from employee e join organization o on e.emp_dept=o.org_teamname
     where emp_id = #{emp_id}
</select>

 

 

6. Entity

  • @Getter, @Setter
  • GetMapping에서 사용하는 1) entity는 파라미터를 넘겨주기 위한 객체와 2) DB꺼내온 값들을 저장할 객체로 나뉜다
  • Entity는 Mapper, Controller와 함께 연동해서 봐야한다.
    Mapper.xml
<select id="getEmpInfo" resultType="Member parameterType="Member">
    select emp_id, emp_name, emp_level, emp_join from employee where emp_dept= #{emp_det}
   <and> emp_posi = #{emp_posi}
</select>

         Cotroller

Member member = new Member(String org_teamanme, String, emp_posi);
service.getEmpInfo(member);

 

          Entity

  • entity는 공유하되, 생성자는 계속해서 추가될 필요가 있음. 같은 순서로 같은 자료형이 오는 게 중요

1) 조건이 많아 파라미터로 여러가지 값들을 넘겨줘야할 때

  • emp_dpet, emp_posi을 인자로 가지는 생성자 추가

2) DB 꺼내온 값 저장 위한 Entity

  • emp_id, emp_name, emp_level, emp_join를 인자로 가지는 생성자 추가
  • 쿼리문에서의 컬럼명[혹은 별칭]과 entity의 필드명이 같아야한다. 같지않으면 resultMap을 사용해 매핑 필요
  • 컬럼명과 동일하지 않으면 as 키워드를 통해 쿼리문에서 맞춰주는 것을 권장

 

 


 

 

스프링 개발 순서 [POST 요청]

1. View단

  • Post는 뷰단이 따로 없고, Get매핑을 통해 뷰단이 출력되면 거기서 받은 입력값을 처리하는 과정
  • Post요청을 처리한 뒤 연결해줄 페이지만 response.sendRedirect: 다른 페이지로 새로운 GET요청을 한다고 생각하면 됨

- **PostMapping [ form 입력값 처리 페이지]** : 입력값을 받아 **UPDATE, DELETE, INSERT**수행

2. Controller

  • 주된 역할 : name값으로 넘어온 자료를 가공해서 service-dao까지 넘겨주는 것
  • ex1. 파일 : multipart처리
  • ex2. 문자열이 넘어오는데, 형태가 2021-01-03 / 2020-10-12 / 2013-08-09 이런식으로 DB에 바로 넣기 힘든 데이터가 넘어올 때 가공
    뷰단에서 넘어올 때는 하나의 문자열이지만, DB에 넣을 때 쪼개서 넣어줄 필요 있음
    split(" / ") -> String 배열에 각각의 날짜값이 저장
  • 데이터 가공이 필요하면 하고 아니면 바로 서비스 함수를 호출한다.
  • Mapper에서는 하나의 객체 자료만을 파라미터로 받을 수 있다.
    Member 엔터티가 되었든, String이든, List이든, HashMap이든 파라미터로 넘겨주기 위해 하나의 객체로 만들어줘야하는데
    이 작업을 보통 Controller에서 함

넘겨주는 데이터에 따른 Controller의 형태

 

1.Entity

XXservice.addMember(new member(emp_id, emp_dept, emp_password, emp_email)); --당연히 생성자 존재해야함

 

 

2. HashMap

HashMap<String, String> map = new HashMap(); 
map.put("emp_id", emp_id) map.put("emp_dept", emp_dept); 
XXservice.addMember(map);

 

3. 원시형

 

4. ArrayList : 같은 작업을 반복하는데, emp_id 등 특정 값만 달라질 때(쿼리문이 동일하고 조건값만 달라질때)

  • 일괄수정, 뷰단에서 반복문 안에 form태그 존재할때 값들이 배열로 넘어옴
  • 잘 안씀
  1. ArrayList list = new ArrayList();
  2. 마찬가지로 for문으로 배열의 값들을 하나씩 빼서 Member객체를 만들어준 뒤 list에 추가
  3. 서비스 함수 호출
for(int i=0; i<emp_id.length(); i++){ 
	Member member = new Member(emp_id[i], emp_name[i], emp_level[i]); list.add(member); 
} 
int result = XXservice.insertInfo(list); 
syso(result) --> 로그에 몇개의 레코드가 변경되었는지 확인 가능, 0으로 뜨면 쿼리에 문제가 있음

3. Service

Service Interface

  • 파라미터, 리턴타입만 신경쓰면 됨
  • 리턴타입은 대다수의 경우 int일 것임
    PostMapping자체가 insert, update, delete을 수행하는 요청이다보니까 몇 개의 레코드가 변경되었는지 그 값을 반환

ServiceImpl

  • 세부 기능단위로 여러 작업 수행
    ex. 직원추가 기능 - 사번생성, 이메일전송, DB에 값넣기
  • 경우에 따라 여러 개의 DAO가 호출될 수도 있음.
  • ex. 직원추가의 경우 서비스단에서 사번 생성, 이메일 전송, DB에 넣기라는 세 가지 기능이 수행되어야 함
  • 추가로 수행되어야 할 작없이 없다면 보통 dao함수를 호출하고 끝
    return memberDao.insertInfo()
  • 예시 : 휴가요청 승인은 휴가요청 테이블의 상태 변경, 근태테이블에 정보 삽입, 직원테이블의 연차변경
    • Service 클래스의 메소드는 confirm하나
      public int confirm(holi_rid){
      	holiDao.confirm(holi_rid);
      	workDao.insertVac(emp_id, holi_code, work_day);
      	memberDao.updateAnnday(emp_id);
      }

4. DAO

  • 파라미터, 리턴타입만 맞추면 된다.

5. Mapper

  • 파라미터만 주의
  • MySql에서 먼저 돌려보고 가져와서 수정
  • resultType은 따로 작성하지 않는다. update, insert, delete의 결과가 항상 int이기 때문에 지정하지 않아도 int로 반환

6. Entity

  • 파라미터에서만 필요할 수도 있고 안할수도 있음