우선 첫번째 전제로 페이지 폼 태그에서


<form action="insert.me" method="POST" enctype="multipart/form-data">

<input type="file" name="photo">

</form>


과 같이 enctype="multipart/form-data"으로 데이터를 전송해야 한다.






두번째 전제로 라이브러리 다운로드


업로드 관련 라이브러리를 pom.xml에 추가하자( https://qdgbjsdnb.tistory.com/237 )




세번째 전제로 업로드 관련 서블릿 xml 설정들


    <!-- 파일 업로드를 하기 위해 bean 등록 -->
    <beans:bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
        <beans:property name="maxUploadSize" value="10000000"></beans:property>
    </beans:bean>


용량 10MB 이하만 등록가능 하기 때문에 10MB보다 조금 적게 max를 설정








구현


    @RequestMapping("insert.me")
    public String insertMember(Member m, Model model, HttpServletRequest request, @RequestParam(value = "photo", required = false) MultipartFile photo) {
        System.out.println("Member : " + m);
        System.out.println("photo : " + photo);
       
        String root = request.getSession().getServletContext().getRealPath("resources");
       
        String filePath = root + "\\uploadFiles";
       
        // 파일명 변경
        String originFileName = photo.getOriginalFilename();
        String ext = originFileName.substring(originFileName.lastIndexOf("."));
        String changeName = CommonUtils.getRandomString();
       
        try {
            photo.transferTo(new File(filePath + "\\" + changeName + ext));
           
            ms.insertMember(m);
           
            return "redirect:goMain.me";
           
        } catch (Exception e) {
            new File(filePath + "\\" + changeName + ext).delete();
           
            model.addAttribute("msg", "회원가입 실패!");
            return "common/errorPage";
        }
    }




주목할 부분은 빨간색 글씨로 입력


ext는 기존의 파일의 확장자를 따옵니다.


DB의 중복을 피하기위해 랜덤 String을 생성해 이름을 변경


photo.transferTo(new File(filePath + "\\" + changeName + ext)); 를 통해 저장


만약 DB 등록에 실패하면 new File(filePath + "\\" + changeName + ext).delete();로 저장된 파일 삭제






사용자가 View를 통해서 로그인 정보(ID, PW)를 입력하여 로그인을 요청했다는 가정하에 진행


Controller에 로그인 정보가 넘어왓을 때







위와 같은 구조로 순서는


View - Controller - try( Service - Dao ), catch(LoginException)


진행


Controller 데이터 처리는 저번 포스트와 같이 하면 된다. ( https://qdgbjsdnb.tistory.com/238 )


소개한 8가지 방법 중 아무거나 써도 된다.


이 중 7번째


@Controller
public class MemberController {

    @Autowired
    private MemberService ms;


    @RequestMapping("login.me")
    public String loginCheck(Member m, Model model, HttpSession session) {
        Member loginUser;
        try {
            loginUser = ms.loginMember(m);
           
            session.setAttribute("loginUser", loginUser);
           
            return "redirect:goMain.me";
        } catch (LoginException e) {
            model.addAttribute("msg", e.getMessage());
           
            return "common/errorPage";
        }
       
        return null;
    }

}




Member 타입 객체 loginUser는


Spring이 객체화 해 준 Service 인터페이스인 MemberService 타입 객체 ms의


loginMember 메소드를 이용하여 로그인 정보(ID, PW)를 넘긴다.




loginUser = ms.loginMember(m);의 loginMembe메소드나 그 하위 메소드에서


throw new LoginException을 하용하기 때문에 try catch문이 필요.





MemberService.java


public interface MemberService {

    Member loginMember(Member m) throws LoginException;

}

MemberServiceImpl.java


@Service

public class MemberServiceImpl implements MemberService {
    @Autowired
    private SqlSessionTemplate sqlSession;
    // 이렇게만 쓰면 Autowired로 생성되지 않기 때문에 xml에 따로 명시해야함
    // root-context.xml에서 진행
    
    @Autowired
    private MemberDao md;


    @Override
    public Member loginMember(Member m) throws LoginException {


        System.out.println(sqlSession.hashCode());


        Member loginUser = md.loginCheck(sqlSession, m);
        return loginUser;
    }

}




SqlSessionTemplate sqlSession은 DB를 연결해주는 라이브러리를 이용한 객체


저번에 설명한 대로 설정파일을 읽어 Spring에서 자동 생성한다. ( https://qdgbjsdnb.tistory.com/236 )


MemberDao md; 또한 컨트롤러에서 Service생성과 똑같이 생성된다.


이번엔 굳이 try catch를 사용하지 않고 throw를 사용






※참고 - 기존의 서블릿 방식으로 객체를 수작업으로 생성하면 .hashcode를


여러번 호출 하였을 때 값이 계속 바뀌는것을 확인 할 수 있다.


Spring에서는 Spring 컨테이너로 생성해준 객체의 .hash코드를


여러번 호출하면 매번 같은 값을 출력하는걸 확인할 수 있다.


이를 통해 같은 객체를 매번 새롭게 생성하는것이 아닌 재사용을 통해


메모리를 좀 더 효율적으로 사용하고 있다고 확인할 수 있다.






MemberDao.java


public interface MemberDao {

    Member loginCheck(SqlSessionTemplate sqlSession, Member m) throws LoginException;

}




MemberDaoImpl.java



@Repository
public class MemberDaoImpl implements MemberDao{

    @Override
    public Member loginCheck(SqlSessionTemplate sqlSession, Member m) throws LoginException {
        Member loginUser = sqlSession.selectOne("Member.loginCheck", m);
       
        System.out.println("Dao Member : " + loginUser);
       
        if (loginUser == null) {
            throw new LoginException("로그인 정보가 존재하지 않습니다.");
        }
       
        return loginUser;
    }

}


MemberDaoImple에서는 SqlSessionTemplat과 myBatis 메소드를 이용해서


DB를 조회해온다.


메소드 명, 사용법은 저번에 설명한 mybatis와 완전 같음.


( https://qdgbjsdnb.tistory.com/228?category=733892, https://qdgbjsdnb.tistory.com/229?category=733892 )


sqlSession.selectOne("Member.loginCheck", m);


조회 결과를 통해 try catch




mybatis와 DB 관련 라이브러리를 이용하기 때문에 pom.xml에 라이브러리 추가는 필수이다.( https://qdgbjsdnb.tistory.com/237 )








LoginException.java의 경우는 간단하게 다음과 같다.


public class LoginException extends Exception{
    public LoginException(String msg) {
        super(msg);
    }
}







이와 같이 Spring을 이용하면 이와같이 코드의 길이가 매우 짧아지고


자동으로 해주는것(프로그래머가 아닌 Spring Container가)이 많아져 편리하다.


특히 누구든 코드 분석하기가 훨씬 쉬워져 다른 사람과 작업에 좋다.


협업에 알맞지만 Spring 구조와 설정 사용법 등 너무 복잡하고 어렵다는 단점이 있다.





다음과 같은 Ajax 요청이 있다고 하자.


    <script type="text/javascript">
        function duplicationCheck() {
            var userId = $("#userId").val();
           
            $.ajax({
                url:"duplicationCheck.me",
                type:"post",
                data:{userId:userId},
                success:function(data){
                    /* alert(data); */
                    console.log(data);
                    console.log(data.userId);
                },
                error:function(status){
                    console.log(status);
                }
               
            });
           
            return false
        }
    </script>





duplicationCheck.me로 ID가 중복되는지 체크해달라는 요청이다.


Controller, Service, Dao 중 Service, Dao 사용방법은 단순 DB 조회로 생략


Controller에서 4가지 방법을 확인해보자.


기본적으로 pom.xml을 통해서 관련 라이브러리가 추가되었음을 전제로 진행 ( https://qdgbjsdnb.tistory.com/237?category=733876 )


또한 관련 라이브러리 추가로 인한 xml 설정


servlet.xml, 서블릿 xml에 다음 태그들을 추가




    <!-- jsonView 설정 -->
    <beans:bean id="jsonView" class="net.sf.json.spring.web.servlet.view.JsonView"></beans:bean>
    <!-- beanNameViewResolver는 없는 자원에 대해 논리적 이름을 가지고 view를 지정한다. -->
    <beans:bean id="viewResolver" class="org.springframework.web.servlet.view.BeanNameViewResolver">
        <beans:property name="order" value="1"></beans:property>
    </beans:bean>
    
    <!-- 메세지 컨버터 추가 -->
    <!-- 자바 객체를 자바스크립트 객체로 바꿔주는 역할을 한다. -->
    <beans:bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></beans:bean>
    <beans:bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <beans:property name="messageConverters">
            <beans:list>
                <beans:ref bean="jacksonMessageConverter"></beans:ref>
            </beans:list>
        </beans:property>
    </beans:bean>


xml 설정은 이것으로 마치고 구현



1. 스트림을 이용, 다른 툴은 이용하지 않고 기본 기능을 통해서 값 전달



    // 1. 스트림을 이용한 ajax 처리
    @RequestMapping("duplicationCheck.me")
    public void duplicationCheck(@RequestParam String userId, HttpServletResponse response) {
        System.out.println(userId);
       
        try {
            response.getWriter().print(false);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



2. ObjectMapper를 이용, 잭슨 라이브러리를 사용


ObjectMapper라는 객체를 생성하여 메소드를 이용해서 객체 자체를 전달



    // 2. ObjectMapper를 이용한 ajax
    @RequestMapping("duplicationCheck.me")
    public void duplicationCheck(@RequestParam String userId, HttpServletResponse response) {
        // 잭슨 라이브러리를 사용함
        ObjectMapper mapper = new ObjectMapper();
       
        Member m = new Member();
       
        m.setUserId(userId);
       
        try {
            response.getWriter().print(mapper.writeValueAsString(m));
        } catch (JsonGenerationException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }




결과 Data 값은 위와 같이 출력된다.


배열이나 객체 타입이 아닌 String 형으로 나열되어 나온다.








3. jsonView를 이용, Controller의 ModelAndView 방식과 유사


반환 타입이 ModelAndView



    // 3. jsonView를 사용한 방식
    @RequestMapping("duplicationCheck.me")
    public ModelAndView duplicationCheck(String userId, ModelAndView mv) {
       
        Member m = new Member();
        m.setUserId(userId);
       
        mv.addObject("member", m);
       
        mv.setViewName("jsonView");
       
        // 한글의 경우 인코딩 처리를 해야함
       
        return mv;
    }




결과는 이와같이 오브젝트 타입으로 안에 배열 형식으로 객체가 담겨있다.





4. @ResponseBody를 이용, HashMap을 통해 원하는값을 보냄




    // 4. @ResponseBody를 이용한 ajax
    @RequestMapping("duplicationCheck.me")
    public @ResponseBody HashMap<String, Object> duplicationCheck(@RequestParam String userId, HttpServletResponse response) {
       
        HashMap<String, Object> hmap = new HashMap<String, Object>();
       
        hmap.put("userId", userId);
       
        return hmap;
    }






결과는 위와 같다.


HashMap을 통해서 원하는 값만 간편하게 보내면 된다.








4가지 방식을 보면 사용방법은 모두 쉽다.


문제는 반환되는 형식이 어떠하냐인데 마음에 드는 방법에 익숙해지거나


상황에 맞는 방법을 골라서 하면 될거같다.





Servlet을 사용할 땐 SHA-512 방식의 암호화를 사용해 보았는데 이번엔


BCrypt 방식을 이용해보자.


예제는 회원가입 시 Controller에서 입력받은 암호를 암호화 처리를 통해 변경하는 작업



MemberController.java


@Controller
public class MemberController {
    // 의존성 주입용 필드 선언
    // 어노테이션 + Interface 타입
    @Autowired
    private MemberService ms;
    // 인터페이스를 상속받는 객체에 @Component나 @Service 어노테이션이 필요
    // 객체 생성 시 new Service와 같은 부분을 Spring에게 맡기는것.
   
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;


    @RequestMapping("insert.me")
    public String insertMember(Member m, Model model) {
       
        System.out.println("member : " + m);
       
        String encPassword = passwordEncoder.encode(m.getUserPwd());
       
        System.out.println(encPassword);
       
        m.setUserPwd(encPassword);
       
        int result = ms.insertMember(m);
       
        if (result > 0) {
            return "redirect:goMain.me";
        } else {
            model.addAttribute("msg", "회원가입 실패!");
            return "common/errorPage";
        }
       
    }

}



먼저 bcrypt란?
DB에 비밀번호를 저장할 목적으로 설계된 알고리즘

단방향 해쉬 함수는 암호화된 메세지를 수학적인 연산을 통해 암호화 된 메세지인 다이제스트를 생성한다.


원본 메세지를 가지고 암호화된 메세지를 복호화 할 수 있는 것을 양방향이라고 하고


암호화된 메세지를 복호화 할 수 없는 것을 단방향이라고 한다.


다이제스트란 결과값들이라고 생각하면 된다.


단방향 해쉬 함수도 사용하면 안되는 이유


1. 단방향 해쉬함수는 많은 다이제스트가 확보되면 평문을 찾아낼 수 있다.


2. 비밀번호를 저장하기 위한 목적으로 설계된 알고리즘이 아닌,


검색을 위해 설계된 알고리즘이다.


그만큼 다이제스트의 생성이 매우 빨라 뚫리는데 시간이 짧다.


이를 해결하기 위해 슬링(salting)기법이 추가되었다.


원본 메세지에 문자열을 추가하여 동일한 길이의 다이제스트를 생성하는 것을 슬링이라 한다.

하지만 salt 값을 알아내면 나머지는 단방향 해쉬함수를 통한 다이제스트를 복호화 하는 것과 별 차이가 없다.


bcrypt 방식은 이러한 salt값을 랜덤하게 생성하여 암호화를 하는 방식이다.


추가적으로 다이제스트를 생성하는데 걸릴 시간을 결정할 수도 있다.


생성 시간에 오래 걸린다면 대량의 다이제스트 생성 역시 오래걸리게 된다.


1999년에 발표되어 현재까지 자주 사용되는 강력한 비밀번호 저장용 매커니즘이다.





설명은 이렇고 사용 자체는 라이브러리 등록, 선언


암호화 메소드 or 암호 일치하는지?에 대한 메소드 선언밖에 없는 간단한 방법이다.






작업 순서로는


메이븐 시큐리티 라이브러리 추가 - Autowired 추가 - security xml 추가 - web.xml에 추가된 xml들 추가이다.







메이븐 시큐리티 라이브러리 pom.xml에 추가한다. - https://qdgbjsdnb.tistory.com/237


Autowired 추가는 빨간글자로 표시해 두었다.



다음은 web.xml 파일 추가부분


/WEB-INF/config/spring-security.xml 추가


    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:root-context.xml
            /WEB-INF/config/spring-security.xml
        </param-value>
    </context-param>



/WEB-INF/config/spring-security.xml의 설정을 읽어오라고 명시하였으니


해당 경로에 파일을 생성한다.


<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
   



    <beans:bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></beans:bean>
   



</beans:beans>






기존에 있는 기본 beans 선언 안에


<beans:bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></beans:bean>


BCryptPasswordEncoder Bean 선언만 하면 끝난다.








메소드의 경우는 BCryptPasswordEncoder 로 선언된 객체를 통해


encode(String형)을 사용하면 String형이 암호화되고


matches(입력한 암호, DB에 저장된 암호)를 입력했을 땐 암호가 일치하는치 체크해준다.(true or false)





        String encPassword = passwordEncoder.encode(m.getUserPwd());



        if (!passwordEncoder.matches(m.getUserPwd(), encPassword)) {
            throw new LoginException("로그인 실패!");
        } else {
            loginUser = md.selectMember(sqlSession, m);
        }






먼저 Servlet 프로젝트에서 사용했던 가장 익숙한 방법.


예제는 전부 login.me라는 맵핑 주소를 통해 연결되고 POST 방식으로 전달 되었다.




1. HttpServletRequest, HttpServletResponse를 이용



    // 1. 서블릿에서 했던 방식대로 HttpServletRequest, HttpServletResponse를
    // 매개변수로 선언하면 스프링 컨테이너가 이 메소드를 실행할 때 자동으로 두 객체를 인자로 주입해준다.
    @RequestMapping("/login.me")
    public String loginCheck(HttpServletRequest request, HttpServletResponse response) {
        String userId = request.getParameter("userId");
        String userPwd = request.getParameter("userPwd");
       
        System.out.println(userId + "  " + userPwd);
       
        return "main/main";
    }



2. 어노테이션 RequestParam을 이용하여 값을 받아오기



    // 2. RequestParam 어노테이션을 이용해서 파라미터 값 받아오기
    // => 스프링에서는 조금 더 간단하게 파라미터를 받아올 수 있는 방법을 제공한다(RequestParam)
    @RequestMapping(value="/login.me", method=RequestMethod.POST)
    public String loginCheck(@RequestParam("userId")String userId, @RequestParam String userPwd) {
        System.out.println(userId + "  " + userPwd);
       
        return "main/main";
    }


@RequestParam("전달받은 name") 처럼 name을 명시해도 되고


아니면 ("")을 생략해도 자동으로 잡히기 때문에 편한 방식으로 사용하면 된다.






3. 어노테이션 RequestParam은 생략이 가능하다.




// 3. RequestParam 어노테이션은 생략해도 파라미터값을 가져와서 매개변수에 저장할 수 있다.
    @RequestMapping(value="login.me", method=RequestMethod.POST)
    public String loginCheck(String userId, String userPwd) {
        System.out.println(userId + "  " + userPwd);
       
        return "main/main";
    }


생략을 통해서 구현하면 본인은 괜찮지만 나중에 봤을 때나 다른사람이 봤을 때


못알아 볼 수 있기 때문에 그냥 명시하는것을 추천





4. @ModelAttribute를 이용, 객체에 자동으로 Setter을 이용하여 값을 가져온다.




    // 4. @ModelAttribute를 이용한 값 전달받는 방법(커맨드 객체)
    @RequestMapping(value="login.me", method=RequestMethod.POST)
    public String loginCheck(@ModelAttribute Member m) {
        System.out.println(m);
       
        return "main/main";
    }

해당 객체에 Setter 메소드가 있어야 되고


전달받은 name과 해당 객체의 필드명이 같아야함.


오타에 주의하자.




5. @ModelAttribute를 생략하는 방법 + 값 리턴방법(예전에 쓰던 request, response)




    // 5. 위의 어노테이션을 생략하고 객체로 받는 방법
    @RequestMapping(value="login.me", method=RequestMethod.POST)
    public String loginCheck(Member m, HttpServletRequest request, HttpServletResponse response) {
        System.out.println(m);
       
        MemberService ms = new MemberServiceImpl(); // 결합을 느슨하게 만듦
       
        try {
            Member loginUser = ms.loginMember(m);
           
            request.getSession().setAttribute("loginUser", loginUser);
           
            return "redirect:goMain.me";
        } catch (LoginException e) {
            request.setAttribute("msg", e.getMessage());
            return "common/errorPage";
        }
       
        return "main/main";
    }


Member m만 입력해도 Spring이 자동으로 맞춰준다.






6. ModelAndView라는 객체를 사용하여 값을 리턴



    // 6. ModelAndView로 리턴 처리
    // => model은 뷰로 전달할 데이터를 앱 형식으로 담을 때 사용하는 객체로 scope는 request이다.
    // => view는 requestDispatcher처럼 forward할 뷰 페이지 정보를 담은 객체이다.
    // => ModelAndView는 이 두가지를 합쳐 놓은 객체이며
    // => Model 객체를 따로 사용하는 것도 가능하다.
    // => 하지만 view 객체는 따로 사용하지 못한다.
    @RequestMapping("login.me")
    public ModelAndView loginCheck(Member m, ModelAndView mv, HttpSession session) {
        try {
            Member loginUser = ms.loginMember(m);
           
            session.setAttribute("loginUser", loginUser);
           
            mv.setViewName("redirect:goMain.me");
           
        } catch (LoginException e) {
            mv.addObject("msg", e.getMessage());
            mv.setViewName("common/errorPage");
        }
       
       
        return mv;
    }


ModelAndView는 리턴 받는 페이지의 정보도 담아가고 리턴 받을 값도 전부 담아서


메소드 리턴타입으로 돌려주는걸 확인 할 수 있다.





7. model 객체로 값을 리턴하고 String 리턴으로 뷰 이동



    // 7. Model 객체를 따로 사용하고 String으로 뷰 이름을 리턴하는 방법
    @RequestMapping("login.me")
    public String loginCheck(Member m, Model model, HttpSession session) {
        Member loginUser;
        try {
            loginUser = ms.loginMember(m);
           
            session.setAttribute("loginUser", loginUser);
           
            return "redirect:goMain.me";
        } catch (LoginException e) {
            model.addAttribute("msg", e.getMessage());
           
            return "common/errorPage";
        }
       
        return null;
    }



8. @SessionAttributes, model 객체를 사용하여 Session 리턴


@SessionAttributes("loginUser")
@Controller
public class MemberController {


    // 8. session에 저장을 할  때 @SessionAttributes 사용하기
    // => 모델에 Attribute가 추가될 때 자동으로 키 값을 찾아 세션에 등록하는 기능을 제공하는
    //    어노테이션이다.
    @RequestMapping("login.me")
    public String loginCheck(Member m, Model model) {
       
        try {
            Member loginUser = ms.loginMember(m);
           
            model.addAttribute("loginUser", loginUser);
           
            return "redirect:goMain.me";
           
        } catch (LoginException e) {
            model.addAttribute("msg", e.getMessage());
            return "common/errorPage";
        }
       
    }

}

@SessionAttributes("loginUser") 라고 명시를 해 두면


model.addAttribute("loginUser", loginUser);라고 추가했을 때 자동으로


Session으로 추가된다.











보너스로 세션 파기하는 방법


    @RequestMapping("logout.me")
    public String LogOutputStream(HttpServletRequest request) {
        request.getSession().invalidate();
       
        return "redirect:goMain.me";
    }


    @RequestMapping("goMain.me")
    public String goMain() {
        // 포워드만 해주는 메소드
        return "main/main";
    }


리턴을 통해서 바로 페이지로 돌아가게 되면


새로고침 시 get, post 값을 한 번 더 전송할 수 있다.


이를 방지하기 위해 포워드만 해주는 메소드로 redirect 우회하면 된다.









※ 위의 메소드들에서 사용하는 ms.***** ms 객체는


@Autowired로 생성한 bean 객체


db에서 로그인 정보를 확인해오는 객체이다.




Spring Framework 부터는 라이브러리를 xml파일에 명시하고


이를 토대로 Maven이라는 툴이 자동으로 다운로드 해서 관리 해줄 것이다.


먼저 해당 파일의 xml 내용을 확인해보자


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.kh</groupId>
    <artifactId>tsp</artifactId>
    <name>testSpringProject</name>
    <packaging>war</packaging>
    <version>1.0.0-BUILD-SNAPSHOT</version>
    <properties>
        <java-version>1.8</java-version>
        <org.springframework-version>5.0.8.RELEASE</org.springframework-version>
        <org.aspectj-version>1.8.13</org.aspectj-version>
        <org.slf4j-version>1.7.25</org.slf4j-version>
    </properties>
    <dependencies>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework-version}</version>
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>

        <!-- AspectJ -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${org.aspectj-version}</version>
        </dependency>

        <!-- Logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${org.slf4j-version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${org.slf4j-version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${org.slf4j-version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.15</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.mail</groupId>
                    <artifactId>mail</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.jms</groupId>
                    <artifactId>jms</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jdmk</groupId>
                    <artifactId>jmxtools</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jmx</groupId>
                    <artifactId>jmxri</artifactId>
                </exclusion>
            </exclusions>
            <scope>runtime</scope>
        </dependency>

        <!-- @Inject -->
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>

        <!-- Servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- Test -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>

        <!---------------------------------------- 필요한 추가 라이브러리는 여기 밑에다 등록하면 됨---------------------------------------->

       
        <!---------------------------------------- 필요한 추가 라이브러리는 여기 밑에다 등록하면 됨---------------------------------------->

    </dependencies>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.9</version>
                <configuration>
                    <additionalProjectnatures>
                        <projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
                    </additionalProjectnatures>
                    <additionalBuildcommands>
                        <buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
                    </additionalBuildcommands>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <compilerArgument>-Xlint:all</compilerArgument>
                    <showWarnings>true</showWarnings>
                    <showDeprecation>true</showDeprecation>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.2.1</version>
                <configuration>
                    <mainClass>org.test.int1.Main</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>








이 파일은 Legacy Project를 생성하면 자동생성 된다.


주목해서 볼 부분은


    <properties>
        <java-version>1.8</java-version>
        <org.springframework-version>5.0.8.RELEASE</org.springframework-version>
        <org.aspectj-version>1.8.13</org.aspectj-version>
        <org.slf4j-version>1.7.25</org.slf4j-version>
    </properties>



Properties의 기본 버전 설정들과


    <dependencies>
        <!-- Spring -->
        <dependency>


dependencies안에서 시작하는 dependency 태그들이다.


dependency 태그들이 우리가 원하는 라이브러리를 명시하는 부분이고


Maven 툴은 이 파일을 읽어 명시한 dependency 태그를 토대로 다운로드 및 업데이트를 진행한다.






이 dependency 태그들은 MVNrepository 라는 페이지에서 복사해서 붙여넣기만 하면 된다.


https://mvnrepository.com/


위 사이트로 들어가 맨 위 검색창에 원하는 라이브러리를 검색하고





ojdbc6을 예로 든다.


원하는 라이브러리를 클릭





원하는 버전을 클릭, 원래는 수십개씩 나열되어 있다.


선택 기준은 Usages, 사용율이 높은 쪽을 선택하는걸 추천한다.


버전이 높다고 무조건 좋은건 아니다.


다른 라이브러리와 충돌이 나는 경우도 많기 때문에...


버전을 클릭하면 다음 화면이 나온다.




이 화면에서 Maven 탭을 누르면 다음과 같이 dependency 태그를 준다.


이 태그를 복사해서 pom.xml에 복사하고 저장하면 Maven 업데이트가 시작된다.





본 블로그에서 사용할 라이브러리들은 다음과 같이 추가하였다.


테스트 당시 충돌은 없었고 몇번의 다운로드 오류로


repository 폴더의 라이브러리들을 몇번 다 지웠다가 다시 다운로드 했다.


충돌이 나거나 다운로드를 잘못 했다는 기준은


Problems 탭에 빨간색 글씨로 오류가 발견되거나


해당 프로젝트를 톰캣에 올리고 시작하면


서버가 다 켜지기도 전에 오류 발생 메세지를 띄운다.


오류 발생 시 그냥 톰캣 오류라고 뜨기 때문에 오류를 찾기 힘들다.


그러므로 편하게 라이브러리 전부 지우고 다시 설치하고 반복하는게 제일 빠르다.




라이브러리가 많아지면 프로젝트가 무거워지고 톰캣 구동시간이 길어질 수 있다.








해당 블로그에서 사용 할 dependency 태그들은 다음과 같다.


        <!-- jdbc 라이브러리 추가 -->
        <dependency>
            <groupId>com.jslsolucoes</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.1.0</version>
        </dependency>

        <!-- 마이바티스 라이브러리 추가 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

        <!-- spring-jdbc 추가 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>

        <!-- 마이바티스 스프링 모듈 추가 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>

        <!-- dbcp 라이브러리 추가 -->
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>

        <!-- 스프링 시큐리티 모듈 추가 -->
        <!-- core, web, config 가 필요하다. -->
        <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>4.2.3.RELEASE</version>
        </dependency>

        <!-- AOP관련 라이브러리 추가 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.4</version>
        </dependency>
       
        <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
       
        <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>
       
        <!-- jackson 추가 -->
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.5</version>
        </dependency>
       
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.5</version>
        </dependency>
       
        <!-- https://mvnrepository.com/artifact/org.codehaus.jackson/jackson-mapper-asl -->
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.9.13</version>
        </dependency>
       
        <!-- Json lib ext 추가 -->
        <!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib-ext-spring -->
        <dependency>
            <groupId>net.sf.json-lib</groupId>
            <artifactId>json-lib-ext-spring</artifactId>
            <version>1.0.2</version>
        </dependency>






다음의 포스트부터는 이 라이브러리들이 정상적으로 받아져서


톰캣 실행이 잘 된다는 가정하에 포스트함




사용한 버전들은 version 태그에 잘 써있음














Spring에서는 객체를 직접 new Service();와 같이 생성하지 않아도 사용할 수 있게 설정할 수 있다.


new Service와 같은 객체화를 Spring에 맡기는 것이다.


- 컨트롤러 -


@Controller
public class MemberController {
    // 의존성 주입용 필드 선언
    // 어노테이션 + Interface 타입
    @Autowired
    private MemberService ms;

    // 인터페이스를 상속받는 객체에 @Component나 @Service 어노테이션이 필요
    // 객체 생성 시 new Service와 같은 부분을 Spring에게 맡기는것.


}




- Service 인터페이스 -


public interface MemberService {

}


- Service -


// Component, Service 둘다 사용 가능
/*@Component*/
@Service
public class MemberServiceImpl implements MemberService {

}


위와 같은 Member java 파일들이 있다고 할 때


컨트롤러에서 객체를 자동 생성해 주는 부분은


@Autowired
private MemberService ms;


이 부분이다.


어노테이션 @Autowired를 위에 명시하고


객체화 할(MemberServiceImpl) Java 파일 상단에


서비스의 경우는


@Component


@Service


Dao의 경우는 @Repository


해당하는 어노테이션을 명시한다.







이런식으로 어노테이션을 명시해두면


@Autowired
private MemberService ms;


Autowired만으로도 MemberService가 MemberServiceImpl 자바 파일로 객체화 되어 생성된다.


Service나 Dao와 같은 Spring framework 에서 사용하는 기본 패턴의 경우는


@Component, @Service, @Repository와 같은 어노테이션으로 충분하다.


이 외에도 작성자가 계속 간단하게 사용하고 싶어서 임의로 만들고 싶다면 XML 파일에 등록해 두면 된다.










보통 Spring에서는 Service 부분에서 SqlSession 이라는 객체를 생성하여 DB에 접근하기 위한


객체를 만들게 된다.


예전에 JDBCTemplate이라고 Properties 파일을 이용해 설정을 받아


객체를 생성하여 DB를 조회한것과 유사하다.


먼저 SqlSession을 만들기 위해서 mybatis 라이브러리를 다운로드 해야한다.


pom.xml 파일을 열어


        <!-- Test -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>



아래에 <dependency> 태그를 추가하자.


        <!-- jdbc 라이브러리 추가 -->
        <dependency>
            <groupId>com.jslsolucoes</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>11.2.0.1.0</version>
        </dependency>

        <!-- 마이바티스 라이브러리 추가 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

        <!-- spring-jdbc 추가 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>

        <!-- 마이바티스 스프링 모듈 추가 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>

        <!-- dbcp 라이브러리 추가 -->
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>


이 태그를 입력하고 파일을 저장하면 Maven이 자동으로 라이브러리 다운로드를 시작한다.


Progress 탭을 보고 다운로드가 완료되면


Problems에서 빨간색으로 된 오류가 있는지 확인해보고


톰캣을 실행시켜 정상적으로 켜지는지 확인해보자.


만약 문제가 있다면 라이브러리가 다운받아지는 폴더인 repository의 폴더들을 삭제하고


프로젝트 우클릭 - Maven - 업데이트를 한번 하고 다시 시도해보자.


오류가 또 난다면 반복







라이브러리가 정상적으로 다운로드 됫다면 root-context xml 파일을 열자.


sqlSessionTemplate을 Bean에 등록하는 작업





    <!-- 데이터베이스 접속에 관련된 클래스들을 bean으로 등록할 수 있다. -->
    <!-- sqlSessionTemplate 등록 -->
    <!-- SqlSessionTemplate 기본 생성자가 비어있지 않기 때문에 아무것도 안넣으면 오류가 난다. -->
    <!-- 생성자 매개변수인 sqlSession을 추가해주자. -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg ref="sqlSession"></constructor-arg>
    </bean>
   
    <!-- sqlSession도 bean으로 등록한다. -->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <property name="dataSource" ref="dataSource"></property>
    </bean>
   
    <!-- dataSource 객체 bean 등록 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
        <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"></property>
        <property name="username" value="DB 계정이름을 써주세요"></property>
        <property name="password" value="DB 계정비밀번호를 써주세요"></property>
        <property name="defaultAutoCommit" value="false"></property>
    </bean>




위의 코드를 xml 파일 중간 부분에 아무대나 추가하면 된다.


자동으로 생성될 객체들의 생성자를 설정하는 부분이다.


sqlSessionTemplate은 sqlSession를 인자로 받아 생성되고


sqlSession은 dataSource를 인자로 받아 생성된다.


dataSource의 Property(매개변수 인자들)를 보면 driver 설정, url 설정, 접속계정 설정, 접속비번 설정


설정을 하고 생성하게 된다.


그리고 Auto Commit을 False로 두었을 때 여러개의 DB 작업을 하면


성공한 곳 까지는 commit이 자동으로 되기 때문에 나중에 트랜잭션(AOP를 이용) 기능을 설정해주어야 한다.




이번에는 bean 태그의 속성들을 보자


<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"></bean>


id, class를 입력받게 된다.


id는 변수명이라고 보면 되고


class의 경우는 내가 생성할 객체의 해당하는 파일의 위치를 말한다.


org.mybatis.spring 위치에 있는 SqlSessionTemplate 파일을 이용해서 생성한다는 의미




이와같이 root-context.xml에 명시를 하고 톰캣을 실행해보면 정상적으로 자동 생성되는 것을 확인 할 수 있다.


활용으로 자기가 만든 객체도 비슷하게 생성할 수 있다.














Spring IoC(Inversion of Control)란,


프로그램을 돌릴 때 객체 생성, 변경 등 관리를 개발자가 아닌


프로그램을 돌리는 컨테이너가 직접 관리하는 것을 말한다.


Spring Framework는 IoC 구조를 통해서 직접 관리를 수행하게 된다.


우리가 XML에 설정할 부분이 많은 이유이다.


Spring에서 관리하는 객체를 Bean이라고 하고 이 객체들을 관리하는 컨테이너를 Bean Factory라고 한다.


이러한 컨테이너들에 의해 객체의 생명주기와 의존성을 관리하고


코드의 구현시간을 단축하게 해 준다.


또한 메모리 사용에도 좀 더 효율적인 것으로 알 고 있다.







Spring DI(Dependency Injection)란


Spring에서 IoC 구현의 핵심 기술로 의존성 주입을 의미한다.


사용하는 객체들을 개발자가 생성하지 않고 개발자가 xml 파일에 설정해둔


xml 기록을 토대로 컨테이너가 객체에 연결해주는 방법이다.


이러한 방법을 사용하면 코드가 단순해지고


객체간의 결합도(종속 관계)가 느슨해진다.


결합도란 한 클래스에서 필드 객체를 생성 시 발생하는 두 객체 간의 관계를 말하며


객체간의 내용이 수정 될 경우 영향을 미치는 정도라고 한다.




이러한 방식은 프로젝트 협업 시 공동 작업을 하기 때문에 이러한 방법을 사용하면 편의성이 높아지고


모든 객체를 xml에서 간편하게 확인할 수 있다.


생성할 객체에는 꼭 getter, setter가 존재해야 한다.


그리고 자동으로 bean 객체를 찾아서(어노테이션) 생성하는 기능을 Bean Scanning이라고 한다.







MVC 구조를 이용한다.


먼저 Spring Framework의 생명주기, 쉽게 말하면 돌아가는 순서를 알아야한다.



이미지 출처 : https://terasolunaorg.github.io/guideline/1.0.1.RELEASE/en/Overview/SpringMVCOverview.html



위 그림을 Spring Framework의 생명주기라고 하는데


쉽게 말해서 그냥 돌아가는 구조를 말한다.


간단하게 Client(사용자) - Dispatcher Servlet - Controller - Service - Dao 순서로 흘러간다.


그리고 중간에 Dispatcher Servlet은 Handler Mapping의 도움을 받아 해당하는 Controller(맵핑주소)를 찾고


Controller는 DB를 조회하던 무었이던 설정해둔 코딩 그대로 역할을 하고


View로 사용자에게 결과를 리턴해준다.


해당하는 View를 찾아주는 기능은 View Resolver라고 한다.





큰 틀은 Client(사용자) - Dispatcher Servlet - Controller - Service - Dao 순서로 돌아가는데


그 외의 도움을 주는 기능들 Dispatcher Servlet, Handler Mapping, View Resolver 등을 우리가


XML 설정 파일을 이용해 설정해두어야 정상적으로 작동한다.







먼저 Java 파일의 소스를 살펴보자.


맨 처음 프로젝트를 생성 했을 때 있는 HomeController.java 파일이다.


import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {
   
    private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
   
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(Locale locale, Model model) {
        logger.info("Welcome home! The client locale is {}.", locale);
       
        Date date = new Date();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
       
        String formattedDate = dateFormat.format(date);
       
        model.addAttribute("serverTime", formattedDate );
       
        return "home";
    }
   
}


Logger 부분은 로그 남기는 부분으로 나중에 따로 게시



먼저 @, 어노테이션을 보자.


@Controller = Spring Framework가 이 파일이 Controller인지 알기 위한 어노테이션


@RequestMapping = 핸들러 맵핑이 @RequestMapping가 써있는 메소드를 미리 찾아 value에 명시한 키워드를 기억하고 있다가


요청이 들어오면 연결시켜 준다.





보통 @RequestMapping는 뒤에 value와 method 인자를 갖고있고 (예 : (value = "/", method = RequestMethod.GET))


value 자리에는 맵핑 주소


method는 GET 방식 전달인지 POST방식 전달인지 정한다.


이 예제는 요청 주소가 '/'라면 GET 방식으로 값을 받아 동작하는 컨트롤러라는 의미이다.


함수명은 마음대로 사용해도 된다.





여기서 리턴 값은 home이라는 문자열이다.


이 리턴값을 이용해 View Resoler가 해당 페이지를 연결시켜 준다.


설정해둔 접두사 + 리턴값 + 접미사 형태로 합쳐져 url을 완성시킨다.


이 설정은 XML 파일에서 확인할 수 있다.


관련 xml 파일 : web.xml, ***-servlet.xml






web.xml 파일을 열어보면 중간에


    <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/config/action-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
       
    <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>



라고 Servlet 설정을 해두었다.


여기서 url-pattern이 /인 경우 /WEB-INF/config/action-servlet.xml 파일의 설정을 이용한다는 의미이고


action-servlet.xml을 열어보면




    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>
   
    <context:component-scan base-package="com.kh.tsp" />




라는 구문으로 등록되어 있다.


beans:bean 태그에서 InternalResourceViewResolver 클래스를 이용(ViewResolver)


접두사 : /WEB-INF/views/


접미사 : .jsp


로 설정하고


Controller는 com.kh.tsp 패키지 경로 아래의 파일들을 모두 스캔한다.






아까 위의 HomeController를 이용해서 home이라는 문자열을 리턴받았다.


그렇다면 view resolver는 다음과 같은 결과로 url를 만든다.


"/WEB-INF/views/" + "home" + ".jsp"


"/WEB-INF/views/home.jsp"







이러한 원리로 Spring Framework에서는


Dispatcher Servlet과 Handler Mapping, Controller가 상호작용하여


작동하게 된다.







context.xml 파일들로 다양한 설정(서블릿 같은 기능을 하는 문구)들을 관리하게 되는데


한 context 파일을 사용하게 되면 설정이 너무 길어지므로


분류를 두어 나누는 것을 추천한다.


이 xml 파일들을 여러개 만들어 관리를 하지만


이 파일들을 읽어오라고 따로 명시를 해주어야 한다.


이에 대한 설정은 web.xml 이라는 파일에서 진행한다.





다음 예시는 root context를 webapp 바깥의 resources로 옮기고


WEB-INF 폴더 아래의 spring 폴더를 config로 이름을 바꾸고


servlet-context파일을 config로 옮긴 뒤 이름을 action-servlet으로 바꾼다.


위의 수정 내역을 web.xml 파일에 등록


먼저 context-param의 내용을 바꾼다.


    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:root-context.xml
        </param-value>
    </context-param>



다음으로는 Servlet 정보, servlet context 정보가 담겨있는


<servlet>태그, <servlet-mapping> 태그를 수정



    <servlet-name>action</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/config/action-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
       
    <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>




이와 같이 servlet-class를 통해서 Dispatcher Servlet을 사용한다고 명시하고


param name과 value를 통해 경로를 입력한다


load-on-startup는 로딩 순서이므로 그냥 적으면 되고


servlet-mapping을 통해서 맵핑 주소를 설정한다.


실행 테스트를 할 때 localhost:포트/키워드를 주소창에 입력했을때


자동으로 맨 뒤에 슬래시가 붙는데 localhost:포트/키워드/


슬래시의 맵핑 주소가 설정 된 곳으로 연결되게 된다.





또 하나의 예제로 member를 통해 맵핑 설정을 이해해보자.


    <servlet>
        <servlet-name>member</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/config/member-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>


    <servlet-mapping>
        <servlet-name>member</servlet-name>
        <url-pattern>*.me</url-pattern>
    </servlet-mapping>



*.me를 통해서 url 이 .me로 끝나는 연결은


/WEB-INF/config/member-servlet.xml 경로의 설정을 읽어서 연결을 한다.





보너스로 이와 같은 방법으로 Dispatcher Servlet을 이용하여 Encoding 타입을 바꾸는 방법은 다음과 같다.


    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>





Servlet에서는 필터 클래스를 생성하여 들어오는 모든 요청(/*)의 인코딩 타입을 UTF-8로 설정했지만


Spring에선 위와같이 명시하면 끝난다.










+ 추가 - context 파일에 들어가는 내용


Spring Framework는 객체를 생성 시 자동으로 new 객체()를 해주도록 설정 할 수 있다.


이 설정은 context.xml 파일들에서 설정하고


이러한 객체(예전에 VO 폴더에 만든 객체들)들을 bean 이라고 한다.


해당하는 서블릿으로 연결되면 bean들을 하나하나 생성 할 필요 없이 바로바로 사용 할 수 있게 설정하는 xml이다.








+ 추가 - context.xml 파일이 어떻게 맵핑 주소를 명시한 java 파일을 찾는가


context.xml의 내용을 보면


<context:component-scan base-package="패키지 명" />


context라는 태그가 존재하고 base-package라는 속성이 등록되어 있다.


base-package에 등록된 경로를 따라서 하위에 존재하는 java 파일들을 전부 스캔하여


@RequestMapping("맵핑 주소") 라는 어노테이션을 사용한 메소드들을 찾는다.


톰캣이 이를 가지고 있다가 맵핑 주소에 요청이 들어오면 해당하는 메소드에 연결해주는 방식이다.


bean 객체를 자동으로 잡아주는 방법도 있고 수동으로 등록을 해야 할 때가 있다.


자동으로 잡아주는 방식은 @Controller, @Service, @Repository와 같은 어노테이션으로 자동으로 잡히게 된다.


자세한 설명은 Autowired를 사용할때 게시합니다.









Window - Preferences에서


ignored Resources를 검색하면 Team 관련 설정이 나온다.


사진과 같이 */target/* 패턴을 추가하면


Spring 프로젝트 컴파일 시 생성되는 target 파일들이 git 업로드에 잡히지 않는다.


하지만 설정을 해도 잘 안먹힐때가 많다....ㅜㅜ










+ Recent posts