위 현상은 IE에서는 나타나지 않는다. 크롬브라우저에서만 나타난다.

 

 문제가 생기는 이유 : 

 

응답헤더에 ContentType 이외에 파일정보를 Header에 추가하는 코드 때문에 나타는 증상이다.  Internet Explore에서는 다운로드에 대한 파일정보를 헤더에 넣어줘도 이상이 없었지만, 크롬에서는 그것을 취약점 공격을 위한 수단으로 판단한 것으로 보인다. 

예제 코드 : 

HttpServletResponse response = (HttpServletResponse) ActionContext.getContext().get(StrutsStatics.HTTP_RESPONSE); response.setContentType("application/octet-stream; charset=utf-8"); try { //다운로드되는 파일의 정보를 헤더에 추가하는 코드 response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(item.getName(), "utf-8") + ";"); } catch (UnsupportedEncodingException ignored) { // do nothing }


 

 해결방법 : application/x-download 를 활용

 

해결 코드 :

HttpServletResponse response = (HttpServletResponse) ActionContext.getContext() .get(StrutsStatics.HTTP_RESPONSE); response.setContentType("application/x-download"); try { HttpServletRequest request = (HttpServletRequest) ActionContext.getContext() .get(StrutsStatics.HTTP_REQUEST); LOG.debug("User-Agent : " + request.getHeader("User-Agent")); if(request.getHeader("User-Agent").contains("Firefox")) { response.setHeader("Content-Disposition", "attachment;filename=\"" + new String(item.getName().getBytes("UTF-8"), "ISO-8859-1") + "\";"); } else { response.setHeader("Content-Disposition", "attachment;filename=\"" + URLEncoder.encode(item.getName(), "utf-8") + "\";"); } } catch (UnsupportedEncodingException ignored) { // do nothing } response.setHeader("Content-Transfer-Encoding", "binary"); LOG.debug("Content-Disposition : " + response.getHeader("Content-Disposition")); LOG.debug("Content Type : " + response.getContentType()); File file = new File(item.getPath()); FileInputStream fileIn = null; ServletOutputStream outstream = null; try { fileIn = new FileInputStream(file); outstream = response.getOutputStream(); byte[] outputByte = new byte[8192]; while (fileIn.read(outputByte, 0, 8192) != -1) { outstream.write(outputByte, 0, 8192); } outstream.flush(); } catch (FileNotFoundException e) { LOG.error(e); } catch (IOException e) { LOG.error(e); } finally { try { fileIn.close(); } catch (IOException e) { } try { outstream.close(); } catch (IOException e) { } }



인터넷을 뒤져봤지만, 쉼표(,)를 다른 문자로 대체하면 된다는 해결책 외에는 딱히 방법이 없었다.

구글을 돌아디나다가 검색해서 찾은 해결책을 기록한다.

출처 : http://stackoverflow.com/questions/2405568/java-webapp-adding-a-content-disposition-header-to-force-browsers-save-as-beh

익스플로러8, 크롬(18.0.1025.151), 파이어폭스(11)에서 정상 동작합니다.

저작자 표시
Posted by 허니몬

metho='post' 를 코딩하ㅣ 않았을 때, 디폴트는 HTTP GET이다. 따라서 브라우저는 파라미터 정보를 몸체가 아니라 헤더에 보낸다는 것이다.

요청이 GET으로 들어온다는 것은 서블릿에 doGet()이 있어야 한다는 말인데, 폼 처리에 대해서 대부분 doPost()만 만들어놓기 때문에

문제가 발생한다.

 

서블릿에서 doGet()과 doPost() 둘 다 지원하려면?

둘다 지원해야하는 경우, 보통 데이터를 수정하는 것이 아니라

doGet()을 구현하고 doPost()는 요청을 doGet()으로 넘기는 방식으로 구현한다.

  1. public void doPost() throws ... {
  2. doGet(request, response);

  3. }

이 글은 스프링노트에서 작성되었습니다.

Posted by 허니몬

인터넷 검색을 통하여 찾아본 구현 방법들은 서블릿에서 저장된 것들을 처리하는 방법들 뿐이었음.

나름 독자적인(?!) 해석을 통하여 JSP로만 구현하는 방법을 터득했다. 이를 이용할 예정임.

현재 진행중인 프로젝트가 완료되면 특별한 기능이 있을 경우, 기재를 하여 설명드리도록 하겠음.

 

JFeeChart 구현과 관련된 JAR 파일들

1) jfreechart-1.0.0-pre2.jar

2) jcommon-1.0.0-pre2.jar

메뉴얼 API : http://www.jfree.org/jfreechart/api/javadoc/index.html

 

  1. <%@ page language="java" contentType="text/html; charset=EUC-KR"  pageEncoding="EUC-KR"%>
    <%@ page import="java.io.*" %>
    <%@ page import="org.jfree.data.general.DefaultPieDataset"%>
    <%@ page import="org.jfree.chart.JFreeChart"%>
    <%@ page import="org.jfree.chart.plot.PiePlot"%>
    <%@ page import="org.jfree.chart.ChartRenderingInfo"%>
    <%@ page import="org.jfree.chart.servlet.ServletUtilities"%>
    <%@ page import="org.jfree.chart.urls.StandardPieURLGenerator"%>
    <%@ page import="org.jfree.chart.entity.StandardEntityCollection"%>
    <%@ page import="org.jfree.chart.ChartFactory"%>
    <%@ page import="org.jfree.chart.ChartUtilities"%>
    <%@ page import="org.jfree.data.general.PieDataset"%>
    <%@ page import="org.jfree.data.category.DefaultCategoryDataset"%>
    <%@ page import="org.jfree.chart.plot.PlotOrientation"%>
    <%@ page import="org.jfree.chart.servlet.*"%>
    <%@ page import="com.oreilly.servlet.*" %>
    <%@ page import="com.oreilly.servlet.multipart.*" %>


    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
    <title>JChart 연습하기</title>
    </head>
    <body>
        <%
            DefaultPieDataset ds = new DefaultPieDataset();
            ds.setValue("홍길동", new Double(40.0));
            ds.setValue("홍길숙", new Double(25.0));
            ds.setValue("기타", new Double(15.0));
           
            JFreeChart chart = ChartFactory.createPieChart("득점분포", ds, true, true, false);
            chart.setBackgroundPaint(java.awt.Color.white);
             chart.setTitle("JChart 연습하기");
             
             ChartRenderingInfo info = new ChartRenderingInfo(new StandardEntityCollection());
           
             
            //PNG 파일명을 만들어내기
             String fileName = ServletUtilities.saveChartAsPNG(chart, 600, 300, info, session);           
           
             System.out.println("파일명 : " +fileName);
             
            //특정 임시 폴더에 이미지 파일을 만들어냄.
             String dir = application.getRealPath("/upload/"); // 파일경로 지정
             dir = "D:\\" + dir.substring(3) + "/";
             String filepath = dir + fileName;
             //System.out.println("File path = "+ filepath);
       
             FileOutputStream fos = new FileOutputStream(new File(filepath));
             File f = new File(filepath);
             ChartUtilities.writeChartAsPNG(fos, chart, 600, 300);
             
             String graphURL = request.getContextPath() + "/upload/" + fileName;
             //System.out.println(graphURL);
            %>
            <img src="<%=graphURL%>"/> <!--//파일 경로와 파일명을 받아서 차트를 보여줌-->

    </body>
    </html>

● 파일명 : jfreechart-2348981821190451270.png (파일명은 ServletUtilities.saveChartAsPNG(chart, 600, 300, info, session); 에 의해서 임의 지정됨)
● File path = D:\workspace\project\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\WomanHappy\upload/jfreechart-2348981821190451270.png  // 웹 서버에 저장된 실제 주소
● /WomanHappy/upload/jfreechart-2348981821190451270.png // 서버에 저장된 주소

JFreeChart.JPG <결과물>

이 글은 스프링노트에서 작성되었습니다.

Posted by 허니몬
2009/06/01 18:24

<%@ page~ %>

<%@ include file %>
<jsp:include page= />

<%@ taglib %>

 

JSTL 소개

<%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/cone" %>
<%@ taglib prefix = "fmt" uri = "http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix = "sql" uri = "http://java.sun.com/jsp/jstl/sql" %>
<%@ taglib prefix = "x" uri = "http://java.sun.com/jsp/jstl/xml" %>

 

코어 라이브러리

<c:out> 태그

  1. <%@ page contentType = "text/html; charset=euckr" %>
    <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
    <html>
    <body>
        <c:set var = "pass" value="${param.pass}" /> <!-- 이건 <c:set> 태그 이다. -->
        <c:out value="${pass}" />
    </body>
    </html>

 

<c:if > 태그

  1. <%@ page contentType = "text/html; charset=euckr" %>
    <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
    <html>
    <body>
        <c:set var = "pass" value="${param.pass}" />
        <c:if test="${!empty pass}">
            Pass value : ${pass}
        </c:if>
        <c:if test="${empty pass}">Please input param...
        </c:if>
        <%-- <c:out value="${pass}" />  --%>
    </body>
    </html>

 

  1. <%@ page contentType = "text/html; charset=euckr" %>
    <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
    <html>
    <body>
        <c:set var = "pass" value="${param.pass}" />
        <c:if test="${!empty pass}">
            Pass value : ${pass}
        </c:if>
       
        <c:if test="${pass eq 'hello'}">, welcome
        </c:if>
       
        <c:if test="${pass ne 'hello'}">Again!!
        </c:if>
       
        <c:if test="${empty pass}">Please input param...
        <!-- pass parameter의 값에 따라 다른 값을 출력하는 것 -->

        </c:if>
    </body>
    </html>

 

<c:if> 태그 이용하여 파라메터에 따라 다른 값 출력하기

  1. <%@ page contentType = "text/html; charset=euckr" %>
    <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
    <html>
    <body>
        <c:set var="score" value="${param.score}" />
            score : ${score}
            <br>
        <c:choose>
            <c:when test="${(100 >= score) && (score >= 90)}">
                <c:out value="'수'를 받으실 수 있겠네요!!" />
            </c:when>
            <c:when test="${(90>score) && (score >=80)}">
                <c:out value="'우'는 받으실 수 있겠네요." />
            </c:when>
            <c:when test="${ (80>score) && (score >= 70) }">
                <c:out value="'미'는 받으실 수 있겠어요. ㅡㅅ-);" />
            </c:when>
            <c:when test="${ (70>score) && (score >= 60) }">
                <c:out value="'양'는 받으실 수 있겠어요. ㅡㅅ-);" />
            </c:when>
            <c:when test="${ (60>score) && (score >= 50) }">
                <c:out value="'가'는 받으실 수 있겠어요. ㅡㅅ-);" />
            </c:when>
            <c:otherwise>
                <c:out value="모르겠네요. ㅡㅅ-); 뭘 입력한거에요? 공부좀 하세요." />
            </c:otherwise>
        </c:choose>

    </body>
    </html>

 

<c:forEach> 태그 이용하여 구구단 표현하기

  1. <%@ page contentType = "text/html; charset=euckr" %>
    <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
    <html>
    <body>
        <table border="1">
        <c:forEach var="x" begin="1" end="9" step="1">
            <tr>
            <c:forEach var="y" begin="1" end="9" step="1">
            <td><c:out value="${y}" /> X <c:out value="${x}" /> = <c:out value="${x*y}"/></td>
            </c:forEach>
            </tr>
        </c:forEach>

        </table>     
    </body>
    </html>

gugudan.JPG

<c:import> 태그 사용

  1. <%@ page language="java" contentType="text/html; charset=EUC-KR" pageEncoding="EUC-KR"%>
    <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <body>
        <h1>이것은 구구단이다.</h1>
        <hr>
        <c:import url="test.jsp" />
        <hr>
    </body>
    </html>

importGugudan.JPG

<c:redirect> 태그 사용

  1. <%@ page contentType = "text/html; charset=euckr" %>
    <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
    <html>
    <body>
        <c:set var = "pass" value="${param.pass}" />
        <c:if test="${!empty pass}">
            Pass value : ${pass}
        </c:if>
      
        <c:if test="${pass eq 'hello'}">, welcome
        </c:if>
      
        <c:if test="${pass ne 'hello'}">
            <c:redirect url="test.jsp" />
        </c:if>
      
        <c:if test="${empty pass}">Please input param...
        <!-- pass parameter의 값에 따라 다른 값을 출력하는 것 -->

        </c:if>
    </body>
    </html>

 

SQL 라이브러리

  1. <%@ page contentType = "text/html; charset=euckr" %>
    <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib prefix = "sql" uri = "http://java.sun.com/jsp/jstl/sql" %>

    <sql:setDataSource
        var="ds"
        driver = "oracle.jdbc.driver.OracleDriver"
        url = "jdbc:oracle:thin:@127.0.0.1:1521:cjedu"
        user = "scott"
        password = "tiger"
    />


    <sql:query dataSource="${ds}" var="rs">
        SELECT * FROM customer
    </sql:query>


    <html>
        <table border = "1" >
            <c:forEach var="row" items="${rs.rows}"> <!-- <sql:query></sql:query> 사이에 있는 결과값이 저장된 rs에서 한 행씩 불러옴
          <!-- for(row:rs.rows) 형태라고 생각하면 된다. -->
                <tr>
                    <td>${row.num}</td>
                    <td>${row.name}</td>
                    <td>${row.address}</td>
                </tr>
            </c:forEach>

        </table>
    </html>               

sqlQuery.JPG

 

<c:include> 태그를 이용해서 처리하는 방법: DataSource 풀 사용

  1. <%@ page contentType = "text/html; charset=euckr" %>
    <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>

    <%@ include file="OracleInfo.jsp" %>
    <sql:query dataSource="jdbc/oracle" var="rs">
        SELECT * FROM store
    </sql:query>

    <html>
        <table border = "1" >
            <c:forEach var="row" items="${rs.rows}">
                <tr>
                    <td>${row.pid}</td>
                    <td>${row.pname}</td>
                    </tr>
            </c:forEach>
        </table>
    </html>               

 

<sql:update> 태그 사용

  1. <%@ page contentType = "text/html; charset=euckr" %>
    <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>

    <%@ include file="OracleInfo.jsp" %>
    <%-- <sql:update dataSource="jdbc/oracle" var="rs">
        INSERT INTO customer VALUES('729','honeymon', 'earth')
     </sql:update>
    --%>

    <html>
        처리가 완료되었습니다.
        <hr>
    <%@ include file="OracleInfo.jsp" %>
    <sql:query dataSource="${ds}" var="rs">
        SELECT * FROM customer
    </sql:query>

    <html>
        <table border = "1" >
            <c:forEach var="row" items="${rs.rows}">
                <tr>
                    <td>${row.num}</td>
                    <td>${row.name}</td>
                    <td>${row.address}</td>
                </tr>
            </c:forEach>
        </table>
    </html>   
    </html>

<sql:transaction> 처리 한 경우

  1. <%@ page contentType = "text/html; charset=euckr" %>
    <%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>

    <%@ include file="OracleInfo.jsp" %>
    <sql:transaction dataSource="${ds}">
     <sql:update var="rs">
        INSERT INTO customer VALUES('730','honeymon', 'earth')
     </sql:update>
    in: %{rs}
    <html>
        처리가 완료되었습니다.
        <hr>
    <sql:query var="rs">
        SELECT * FROM customer
    </sql:query>
    </sql:transaction>

        <table border = "1" >
            <c:forEach var="row" items="${rs.rows}">
                <tr>
                    <td>${row.num}</td>
                    <td>${row.name}</td>
                    <td>${row.address}</td>
                </tr>
            </c:forEach>
        </table>
    </html>

 

 

이 글은 스프링노트에서 작성되었습니다.

Posted by 허니몬
2009/05/25 08:44

쿠키(Cookie)

사용자의 ID 혹은 암호까지 기억하는 경우를 보았을 것이다. 이것은 웹 브라우저가 ID 혹은 암호를 사용자의 컴퓨터에 저장하고 있기 때문에 가능하다. 이처럼 웹 서버가 전달한 정보를 웹 브라우저가 컴퓨터에 저장하는 것을 쿠키(Cookie)라고 한다. 쿠키는 다양하게 활용될 수 있는데, 대표적인 사용 예로 사용자 ID와 암호를 기억함으로써 사이트를 편리하게 사용할 수 있도록 하거나 혹은 사용자의 방문 횟수를 기억하돌고 함으로써 사용자의 웹 사이트 사용 유형을 파악하는 것이 있다. 쿠키는 넷스케이프사에서 처음 제안되었고, RFC 2109로 문서화 되었다.

● 관련정보 : http://en.wikipedia.org/wiki/HTTP_cookie (영문)

● 관련정보 : http://ko.wikipedia.org/wiki/HTTP_쿠키 (한글, 간략함. 영문 참조바람)

 

※ 쿠키 : 서버가 자신이 필요한 정보를 클라이언트에 심어놓는 것. 클라이언트가 접속시 자신(서버)이 심어놓은 쿠키 파일을 불러온다. 사용자 ID와 비밀번호, 방문횟수, 사용 패턴등을 분석 가능하다.

230px-Choco_chip_cookie.jpg

  • MS IE : Cookie 라는 폴더
  • 파폭 등은 cookie.txt
  • 쿠키 제한 : 모두 300개 까지만 사용가능
  • 쿠키 최대 크기 : 4KB

 

● 서블릿/JSP는 쿠키를 위해서 Cookie 클래스를 제공한다. 쿠키를 웹 브라우저에 전달하기 위해서는 HttpServletResponse의 addCookie() 메소드를 이용한다.

쿠키 설정하기 절차(심기)

[1]. Cookie 객체를 만든다.

[2]. 쿠키에 속성을 부여한다.

[3]. 쿠키를 전송한다.

Cookie c1 = new Cookie("id", "honeymon");

c1.setMaxAge(-1); // 브라우저가 끄면 사라진다.

res.addCookie(c1);

ex) Cookie(String name, String value) 쿠키의 이름, 값이 필요하다.

  • void setMaxAge(int expiry) 쿠키의 유효한 기간을 설정(단위 : 초)
  • void setValue(String newValue) 새로운 쿠키 값을 설정한다.

    • 정수로 변환시 Integer.parseInt(); 를 사용해준다.

 

Cookie c1 = new Cookie("id", "honeymon");

c1.setMaxAge(60 * 60 * 24 * 365); => 1년간 유지됨

c1.setMaxAge(-1); // 브라우저가 끄면 사라진다.

 

웹 브라우저에 쿠키 전송하기

쿠키의 속성값이 설정되면, 서블릿/JSP는 HttpServletResponse 클래스를 이용해서 웹 브라우저에 쿠키를 설정한다. 이 때 HttpServletResponse으 addCookie() 메소드를 사용한다.

  1. response.addCookie(cookie); --> res.addCookie(c1);

 

쿠키 정보 얻기(뽑기!)

클라이언트에 설정된 쿠키 정보는 웹 브라우저가 다시 웹 사이트를 방문할 때 웹 브라우저에 의해 자동적으로 웹 서버에 전달한다. 쿠키로부터 정보를 얻어 오려면 다음과 같은 절차를 따른다.

[1] 사용자의 요청에서 모든 쿠키를 얻는다.

[2]. 원하는 쿠키를 이름을 통해서 얻는다.

 

 

[3]. 찾은 쿠키로부터 값을 얻는다.

  1. Cookie[] cookies =  request(=req).getCookies();
  2. for ( Cookie mycookie : cookie ) {
       String n = mycookie.getName();
  3. }
    String value = mycookie.getValue();

 

ex) 서블릿/JSP 에서 쿠키 얻기
  1. Cookie[] cookies =  request(=req).getCookies();

 

ex) 쿠키의 이름을 이용해서 원하는 쿠키 찾기
  1. for ( Cookie mycookie : cookie ) {
       String n = mycookie.getName();
    }

 

ex) 쿠키의 값 추출하기
  1. String value = mycookie.getValue();

 

CookieTest.java

  1. import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;

    public class CookieTest extends HttpServlet {
        PrintWriter out;

        protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
            out = res.getWriter();
            res.setContentType("text/html; charset=euc-kr");

            Cookie[] cookies = req.getCookies();

            if ( cookies != null ) {
                for ( Cookie cookie : cookies ) {
                    String name = cookie.getName();
                    String value = cookie.getValue();
                    out.println("Cookie name : "+name+"<br>");
                    out.println("Cookie value : "+value+"<hr>");
                }
                out.println("<a href=CookieTest>다시방문하기</a>");
            } else {
                out.println("No cookies..");
                setCookies(res);
            }
        }

        public void setCookies(HttpServletResponse res){
            Cookie logCookie = new Cookie("login", "honeymon");
            logCookie.setMaxAge(-1);

            Cookie passCookie = new Cookie("pass", "wow");
            passCookie.setMaxAge(-1);

            res.addCookie(logCookie);
            res.addCookie(passCookie);

            out.println("<hr>cookie가 setting 되었습니다.<hr><br>");
            out.println("<a href=CookieTest>돌아가기</a>");
        }

    }

 

CookieTest.java(수정판 : 읽을 때마다 Count를 증가시킴)

  1. import java.io.*;
    import javax.servlet.*;
    import javax.servlet.http.*;

    public class CookieTest extends HttpServlet {
        PrintWriter out;

        protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
            out = res.getWriter();
            res.setContentType("text/html; charset=euc-kr");

            Cookie[] cookies = req.getCookies();

            if ( cookies != null ) {
                for ( Cookie cookie : cookies ) {
                    String name = cookie.getName();
                    String value = cookie.getValue();
                    out.println("Cookie name : "+name+"<br>");
                    out.println("Cookie value : "+value+"<hr>");

                    if ( cookie.getName().equals("count") ) {
                        //value에 1을 더하고 새로운 value를 세팅
                        value = Integer.toString(Integer.parseInt(cookie.getValue()) + 1);
                        //새값이 설정된 쿠키를 브라우저로 전송
                        Cookie countCookie = new Cookie("count",value);
                        res.addCookie(countCookie);

                        //cookie.setValue(value);
                        //res.addCookie(cookie);  이녀석도 된다. ㅡㅅ-)>
                    }
                }
                out.println("<a href=CookieTest>다시방문하기</a>");
            } else {
                out.println("No cookies..");
                setCookies(res);
            }
        }

        public void setCookies(HttpServletResponse res){
            Cookie logCookie = new Cookie("login", "honeymon");
            logCookie.setMaxAge(-1);

            Cookie passCookie = new Cookie("pass", "wow");
            passCookie.setMaxAge(-1);

            Cookie countCookie = new Cookie("count","0");
            countCookie.setMaxAge(-1);

            res.addCookie(logCookie);
            res.addCookie(passCookie);
            res.addCookie(countCookie);

            out.println("<hr>cookie가 setting 되었습니다.<hr><br>");
            out.println("<a href=CookieTest>돌아가기</a>");
        }

    }

CookieTestRun.JPG CookieTest.JPG

이 글은 스프링노트에서 작성되었습니다.

Posted by 허니몬
이전버튼 1 2 이전버튼