SDS_haein
570
2018-08-22 13:43:35 작성 2018-08-23 09:54:48 수정됨
43
5788

웹소켓 JSON방식 전송


웹소켓입니다.

화면이 두개입니다. 사용자화면(A.jsp, C.jsp 등)과 B.jsp화면.

현재 A.jsp에서 등록이나 목록으로 버튼을 누르면 리스트페이지(C.jsp)로 가면서

데이터가 갱신(메시지 전송)되어(sendMessage)

D3.js 차트페이지인 B.jsp에서 비동기로 setInterval하여 페이지를 reload해주는 방식으로 데이터를

실시간으로 갱신시켜줍니다.(onMessage)

setInterval은 1500으로 1.5초마다 페이지를 계속 reload하는게 아니라 reload는 단 한번만 됩니다.

즉, 사용자가 A.jsp에서 이벤트를 실행시킬때(등록하거나 목록으로 갈때)만 B.jsp가 reload되며

이후, A.jsp 빠져나왔기 때문에 웹소켓 연결이 해제 되기때문이죠.(onClose)


하지만 위 방식은 제가 생각하는 웹소켓 구현이 아닙니다.

페이지(B.jsp)가 reload를 하지않고 JSON데이터를

일반적인 웹소켓채팅처럼 reload되지 않고 데이터를 화면에서 얻고 싶습니다.

어떻게 해야할까요?ㅜㅜ


설명이 많이 복잡한 것 같네요.

댓글달아주시면 바로 추가설명하겠습니다.

해결해주신분에게  치킨 기프티콘 드리겠습니다.

오늘 저녁 치킨은 어떠세요?

선생님들 도와주세요 ㅜㅜ


A.jsp

...
<script type="text/javascript">
var dataset = new Array();
<c:forEach var="vo" items="${listses}" >
    var DBDataObject = {x : ${vo.bNo}, y : ${vo.bHit} }; // db data -> javascript object
    var JSONObject = JSON.stringify(DBDataObject); // javascript object -> json data
    dataset.push(DBDataObject);
</c:forEach>

  $(document).ready(function() {
               $("#sendBtn1").click(function() {
                       sendMessage();
                       $('#message').val('')
              	});

               $("#sendBtn2").click(function() {
                   sendMessage();
                   $('#message').val('')
           		});


               $("#message").keydown(function(key) {
                       if (key.keyCode == 13) {// 엔터
                              sendMessage();
                              $('#message').val('')
                       }
               });
        });


        let sock = new SockJS("<c:url value="/echo"/>");
        sock.onmessage = onMessage;
        sock.onclose = onClose;


        function sendMessage() {
               sock.send($("#message").val());
        }

</script>

</head>
<body topmargin="160">
	<div align="center">
		<h2>글쓰기 페이지</h2>
		<hr width="800" size="3" color="#002F75" size="1" />
		<table width="600" cellpadding="1" cellspacing="0" border="0">

			<form:form commandName="BVO" action="writeOk" method="post">

				...
					<td class="line" colspan="2" height="50">
					<span class="yellowbtn" id="sendBtn1" onclick="location.href='list'"><font face="나눔고딕"> 목록</font></span>
					&nbsp;&nbsp;&nbsp;
					<input type="submit" class="yellowbtn" id="sendBtn2" value="등록"></td>
				</tr>
			</form:form>
		</table>
	</div>
</body>
</html>



B.jsp

...
<script type="text/javascript">
//  $(function(){

var dataset = new Array();
<c:forEach var="vo" items="${listses}" >
    var DBDataObject = {x : ${vo.bNo}, y : ${vo.bHit} }; // db data -> javascript object
    var JSONObject = JSON.stringify(DBDataObject); // javascript object -> json data
    dataset.push(DBDataObject);
</c:forEach>
// 	 });

/*
 * web socket 통신
 */
//웹소켓을 지정한 url로 서버 연결.
let sock = new SockJS("<c:url value="/echo"/>");
sock.onmessage = onMessage;
sock.onclose = onClose;

/*
// 메시지 전송
function sendMessage() {
	sock.send($("#message").val());
}
 */
// 메시지 처리
function onMessage(msg) {
    	setInterval("location.reload()",1500);
    }

// 서버 연결 해제
function onClose(evt) {
	[dataset].append("연결 끊김");
	}





//...d3 코딩부분.../

</script>
</body>
</html>



EchoHandler.java


...

public class EchoHandler extends TextWebSocketHandler {
	  private static Logger logger = LoggerFactory.getLogger(EchoHandler.class);
	  private List<WebSocketSession> sessionList = new ArrayList<WebSocketSession>();

	  // 클라이언트와 연결 이후에 실행되는 메서드
	  @Override
	  public void afterConnectionEstablished(WebSocketSession session) throws Exception {
	    sessionList.add(session);
	    logger.info("{} 연결됨", session.getId());
	  }

	  // 클라이언트가 서버로 메시지를 전송했을 때 실행되는 메서드
	  @Override
	  protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
	    logger.info("{}로 부터 {} 받음", session.getId(), message.getPayload());
	    for (WebSocketSession sess : sessionList) {
	      sess.sendMessage(new TextMessage(session.getId() + " : " + message.getPayload()));
	    }
	  }

	  // 클라이언트와 연결을 끊었을 때 실행되는 메소드
	  @Override
	  public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
	    sessionList.remove(session);
	    logger.info("{} 연결 끊김", session.getId());
	  }


	}


0
  • 답변 43

  • ShipJH
    1k
    2018-08-22 14:14:42

    ajax와 함께 폴링, 롱폴링 방식으로 처리하시면 어떨까요 

    http://clearpal7.blogspot.com/2016/06/vs.html

  • SDS_haein
    570
    2018-08-22 14:20:00 작성 2018-08-22 14:20:27 수정됨

    ShipJH 폴링은 동기방식으로 주기적으로 새로고침하는 방식이여서 당연히 사용하지 않을 것이고,
    롱폴링은 비동기지만 서버를 계속 열어두는 개념이라 서버에 무리가 있어
    웹소켓보다 비효율적일 것 같습니다.

  • odyssey320
    792
    2018-08-22 14:25:00

    onMessage에서 msg를 넣고싶은데다가 html로 넣어주시면 땡아닌가요?페이지 새로고침은 왜하는지 잘 이해가안되네요;;

  • SDS_haein
    570
    2018-08-22 14:28:47

    odyssey320 네; 저도 새로고침은 원하지않습니다. 웹소켓처럼 새로고침없이 데이터를 띄워주고싶어요.


    function onMessage(msg) {

    setInterval("location.reload()",1500);

        }

    그럼 이부분을 html로 어떻게 하면되나요?

    제바ㅓㄹ

  • odyssey320
    792
    2018-08-22 14:35:32 작성 2018-08-22 14:36:13 수정됨

    msg를 어디다가 뿌려주면되는건가요?

    jqeury 쓰시니깐,

    function onMessage(msg) {

       $("원하는곳").html(msg);

        }

  • SDS_haein
    570
    2018-08-22 14:48:29 작성 2018-08-22 14:52:27 수정됨

    odyssey320 제가 D3JS 차트구현식 인데... 그걸 쓸수있나요?ㅠㅠ


    ....
    <!-- jQuery CDN-->
    <script src="http://code.jquery.com/jquery-latest.min.js"></script>
    
    <!-- D3 chart CDN -->
    <script src="https://d3js.org/d3.v4.min.js"></script>
    
    <!-- Web socket CDN -->
    <script src="http://cdn.sockjs.org/sockjs-0.3.4.js"></script>
    <script type="text/javascript"src="<c:url value="/resources/js/sockjs.js"/>"></script>
    <script type="text/javascript"src="<c:url value="/resources/js/sockjs.min.js"/>"></script>
    <script type="text/javascript">
    //  $(function(){
    
    var dataset = new Array();
    <c:forEach var="vo" items="${listses}" >
        var DBDataObject = {x : ${vo.bNo}, y : ${vo.bHit} }; // db data -> javascript object
        var JSONObject = JSON.stringify(DBDataObject); // javascript object -> json data
        dataset.push(DBDataObject);
    </c:forEach>
    // 	 });
    
    /*
     * web socket 통신
     */
    //웹소켓을 지정한 url로 서버 연결.
    let sock = new SockJS("<c:url value="/echo"/>");
    sock.onmessage = onMessage;
    sock.onclose = onClose;
    
    /*
    // 메시지 전송
    function sendMessage() {
    	sock.send($("#message").val());
    }
     */
    // 메시지 처리
    function onMessage(msg) {
       	setInterval("location.reload()",1500);
        }
    
    // 서버 연결 해제
    function onClose(evt) {
    	[dataset].append("연결 끊김");
    	}
    
    
    
    
    
    var margin = {left: 40, top: 30, right: 10, bottom: 50};
    var svg = d3.select("svg");
    var width  = parseInt(svg.style("width"), 10)  - margin.left - margin.right;
    var height = parseInt(svg.style("height"), 10) - margin.top  - margin.bottom;
    var svgG = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    var xScale = d3.scaleBand()
    .domain(dataset.map(function(d) { return d.x;} ))
    //.range([0, width]).padding(0.2);
    .range([0, 900]).padding(0.2) //top
    var yScale = d3.scaleLinear()
    .domain([0, d3.max(dataset, function(d){ return d.y; })])
    .range([height, 0]);
    
    svgG.append("g")
    	.attr("class", "grid")
    	.attr("transform", "translate(0," + height + ")")
    	.call(d3.axisBottom(xScale)
    			.tickSize(-height)
    			)
    			.selectAll("text")
    				.attr("class", "axis")
    			    .style("text-anchor", "end")
    			    .attr("dx", "-.8em")
    			    .attr("dy", ".15em")
    			    .attr("transform", "rotate(-65)");
    
    svgG.append("g")
    	.attr("class", "grid")
    	.call(d3.axisLeft(yScale)
    	.ticks(5)
    	)
    			.selectAll("text")
    				.attr("class", "axis");
    
    
    var line = d3.line()
    	.x(function(d) {return xScale(d.x) +20; })
    	.y(function(d) {return yScale(d.y) ; });
    
    var lineG = svgG.append("g")
    	.selectAll("g")
    	.data([dataset]) // dataset
    		.enter().append("g");
    
    
    lineG.append("path")
    	.attr("fill", "none")
    	.attr("stroke", "royalBlue")
    	.attr("stroke-width", "1.5px")
    	.attr("d", line)
    
    
    //outer dot
    lineG.selectAll("dot2")
    	.data(function(d) {return d })
    	.enter().append("circle")
    		.attr("fill", "#00008b")
    	    .attr("r", 5)
    	    .attr("cx", function(d) { return xScale(d.x) +20;})
    	    .attr("cy", function(d) { return yScale(d.y) ;})
    	    .on("mouseover", function() { tooltip.style("display", null); })
    	    .on("mouseout",  function() { tooltip.style("display", "none"); })
    	    .on("mousemove", function(d) {
    	        tooltip.style("left", (d3.event.pageX+10)+"px");
    	        tooltip.style("top",  (d3.event.pageY+0)+"px");
    	        tooltip.html("<b>" + "No. " + d.x + "<br/>" + "Hit : " + d.y);
    	    });
    
    //inner dot
    lineG.selectAll("dot1")
    	.data(function(d) {return d })
    	.enter().append("circle")
    		.attr("fill", "#fff")
    	    .attr("r", 3)
    	    .attr("cx", function(d) { return xScale(d.x) +20;})
    	    .attr("cy", function(d) { return yScale(d.y) ;})
    	    .on("mouseover", function() { tooltip.style("display", null); })
    	    .on("mouseout",  function() { tooltip.style("display", "none"); })
    	    .on("mousemove", function(d) {
    	        tooltip.style("left", (d3.event.pageX+10)+"px");
    	        tooltip.style("top",  (d3.event.pageY+0)+"px");
    	        tooltip.html("<b>" + "No. " + d.x + "<br/>" + "Hit : " + d.y);
    	    });
    
    var tooltip = d3.select("body")
    	.append("div")
    	.attr("class", "toolTip")
    	.style("display", "none");
    
    </script>
    </body>
    </html>


  • SDS_haein
    570
    2018-08-22 14:56:50

    아 슬푸다 ㅜㅜ

  • odyssey320
    792
    2018-08-22 14:59:16

    js차트 많은데 이런식의 차트는 참;;

    일단 a페이지에서 어떤 값을 넣고 등록누르면

    c페이지(또다른 사용자) list에 데이터가 갱신되고

    b페이지의 차트를 c페이지에서 새로고침한다는건가요?


  • SDS_haein
    570
    2018-08-22 15:02:09 작성 2018-08-22 15:06:18 수정됨

    화면이 두개입니다.
    b페이지는 항상띄워져있습니다.

    다른화면(사용자화면)에서  a페이지에서 등록을 누르면
    c페이지(list페이지)로 넘어가게됩니다.
    물론 데이터도 페이지가 c페이지로 넘어가면서 갱신이 되구요.

    그럼 a페이지에서 버튼을 눌렀기때문에 b페이지(차트) 화면은 자동으로 새로고침되어 갱신됩니다.
    따라서 b페이지는 실시간갱신처럼 보입니다.(하지만 새로고침은 하기시름ㅜㅜ)

    이해가 되셨나요?

  • odyssey320
    792
    2018-08-22 15:08:00 작성 2018-08-22 17:41:36 수정됨

    말씀하신 내용이,


    b차트에서 msg데이터를 받아서 쓰는게아니라 

    b차트에서는 a 나 c가 action을 했다는 신호만 받으면(onMessage) 현재 페이지를 새로고침한다.


    이건가요?

  • SDS_haein
    570
    2018-08-22 15:14:09 작성 2018-08-22 15:14:48 수정됨

    네. 선생님 명확하십니다.ㅜㅜ
    뭔가 소통이되어서 기뻐요 ㅜㅜㅜㅜㅜ

    action신호를 받으면(sendMessage),
    onMessage 처리를 합니다.


    근데 이제 sendMessage나 onMessage 를 고치거나
    java 서버단에서 handleTextMessage부분을 고쳐야할 것 같은데...
    감이 안잡힙니다.ㅠㅠ

  • 더미
    15k
    2018-08-22 15:15:37

    이분 이거 아직도 안 됬나요....


    B.jsp 에서

    이부분은 자바스크립트로 바꾸고

    var dataset = new Array();

    <c:forEach var="vo" items="${listses}" >

        var DBDataObject = {x : ${vo.bNo}, y : ${vo.bHit} }; // db data -> javascript object

        var JSONObject = JSON.stringify(DBDataObject); // javascript object -> json data

        dataset.push(DBDataObject);

    </c:forEach>

    // });

    d3  처리부분하고 같이

    var svg = d3.select("svg");

    var width  = parseInt(svg.style("width"), 10)  - margin.left - margin.right;

    var height = parseInt(svg.style("height"), 10) - margin.top  - margin.bottom;

    var svgG = svg.append("g")

    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var xScale = d3.scaleBand()

    .domain(dataset.map(function(d) { return d.x;} ))

    //.range([0, width]).padding(0.2);

    .range([0, 900]).padding(0.2) //top

    var yScale = d3.scaleLinear()

    .domain([0, d3.max(dataset, function(d){ return d.y; })])

    .range([height, 0]);


    svgG.append("g")

    .attr("class", "grid")

    .attr("transform", "translate(0," + height + ")")

    .call(d3.axisBottom(xScale)

    .tickSize(-height)

    )

    .selectAll("text")

    .attr("class", "axis")

        .style("text-anchor", "end")

        .attr("dx", "-.8em")

        .attr("dy", ".15em")

        .attr("transform", "rotate(-65)");


    svgG.append("g")

    .attr("class", "grid")

    .call(d3.axisLeft(yScale)

    .ticks(5)

    )

    .selectAll("text")

    .attr("class", "axis");



    var line = d3.line()

    .x(function(d) {return xScale(d.x) +20; })

    .y(function(d) {return yScale(d.y) ; });


    var lineG = svgG.append("g")

    .selectAll("g")

    .data([dataset]) // dataset

    .enter().append("g");



    lineG.append("path")

    .attr("fill", "none")

    .attr("stroke", "royalBlue")

    .attr("stroke-width", "1.5px")

    .attr("d", line)



    //outer dot

    lineG.selectAll("dot2")

    .data(function(d) {return d })

    .enter().append("circle")

    .attr("fill", "#00008b")

        .attr("r", 5)

        .attr("cx", function(d) { return xScale(d.x) +20;})

        .attr("cy", function(d) { return yScale(d.y) ;})

        .on("mouseover", function() { tooltip.style("display", null); })

        .on("mouseout",  function() { tooltip.style("display", "none"); })

        .on("mousemove", function(d) {

            tooltip.style("left", (d3.event.pageX+10)+"px");

            tooltip.style("top",  (d3.event.pageY+0)+"px");

            tooltip.html("<b>" + "No. " + d.x + "<br/>" + "Hit : " + d.y);

        });


    //inner dot

    lineG.selectAll("dot1")

    .data(function(d) {return d })

    .enter().append("circle")

    .attr("fill", "#fff")

        .attr("r", 3)

        .attr("cx", function(d) { return xScale(d.x) +20;})

        .attr("cy", function(d) { return yScale(d.y) ;})

        .on("mouseover", function() { tooltip.style("display", null); })

        .on("mouseout",  function() { tooltip.style("display", "none"); })

        .on("mousemove", function(d) {

            tooltip.style("left", (d3.event.pageX+10)+"px");

            tooltip.style("top",  (d3.event.pageY+0)+"px");

            tooltip.html("<b>" + "No. " + d.x + "<br/>" + "Hit : " + d.y);

        });


    var tooltip = d3.select("body")

    .append("div")

    .attr("class", "toolTip")

    .style("display", "none");





    // 메시지 처리

    function onMessage(msg) {

       이안으로 들어가야됩니다.

        }


      // 클라이언트가 서버로 메시지를 전송했을 때 실행되는 메서드

      @Override

      protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {

        logger.info("{}로 부터 {} 받음", session.getId(), message.getPayload());

        for (WebSocketSession sess : sessionList) {

          sess.sendMessage(new TextMessage(session.getId() + " : " + message.getPayload()));

     여기에 dataset인 listses  보내주시는 거 구현하고


        }

      }

    // 메시지 전송

    function sendMessage() {

    여기에 setinteval 로 dataset 보내주게 추가 구현하세요.

    }



  • SDS_haein
    570
    2018-08-22 15:21:52

    더미 호곡...감사합니다. 해보겠습니다. 으샤으샤


    근데 마지막

    function sendMessage() {

    여기에 setinteval 로 dataset 보내주게 추가 구현하세요.

    }

    이부분은 어디에 들어가는 건가요?


  • 더미
    15k
    2018-08-22 15:22:54

    주석있는 부분이요

  • odyssey320
    792
    2018-08-22 15:28:34 작성 2018-08-22 15:29:26 수정됨

    제가 만약 작성자라면,


    a에서 입력 데이터를 전송하고


    b차트에서는 데이터를 받아서


    dataset에 push하고 차트를 렌더링하는식으로 짤꺼같네요.


    function onMessage(msg) {

    //이함수에서 msg값 어떤방식으로 받는지 알 수 있을까요?

                            console.log(msg)

    }

  • SDS_haein
    570
    2018-08-22 15:31:44 작성 2018-08-22 15:37:27 수정됨

    더미 그럼 A.jsp에 들어가야한다는거네요. 감사합니다.

    그리고 c:forEach는 자바스크립트로 바꾸게되면 그럼 vo객체에서 어떻게 받아와여?

    JSON,parse를 써야나요?

  • SDS_haein
    570
    2018-08-22 15:34:29

    odyssey320 그냥 값을 받으면 새로고침하는 형식으로 처리하고 있었습니다.

    function onMessage(msg) {

        setInterval("location.reload()",1500);

        }



  • odyssey320
    792
    2018-08-22 15:37:36

    setInterval("location.reload()",1500);

    이자리에

    alert(msg);

    뭐라고 뜨는지 알면 디테일하게 접근하기 쉬울꺼같네요.


  • SDS_haein
    570
    2018-08-22 15:40:44 작성 2018-08-22 15:41:33 수정됨
    message를 주는 id값이 없어서 alert나 console시 [object Object] 라고 뜹니다.
  • odyssey320
    792
    2018-08-22 15:44:06 작성 2018-08-22 15:44:38 수정됨

    그건 값이 있다는거니깐,

    alert(JSON.stringify(msg));

    이렇게 넣고 다시보여주세요.

  • SDS_haein
    570
    2018-08-22 15:46:35

    이런식으로 console에 에러가 뜹니다.

    VM1921:1 Uncaught SyntaxError: Unexpected token o in JSON at position 1
        at JSON.parse (<anonymous>)
        at Function.m.parseJSON (VM666 jquery-latest.min.js:4)
        at r.onMessage [as onmessage] (test3:172)
        at r.dispatchEvent (VM675 sockjs.min.js:2)
        at VM675 sockjs.min.js:2
        at Array.forEach (<anonymous>)
        at r._transportMessage (VM675 sockjs.min.js:2)
        at r.emit (VM675 sockjs.min.js:2)
        at WebSocket.ws.onmessage (VM675 sockjs.min.js:3)


  • odyssey320
    792
    2018-08-22 16:02:09

    json으로 받지를 않으시나보네요.

    흠냐..그럼 a페이지에서

    값을 넣고 등록 눌렀을때 $("#message").val())에는 어떤값이 들어가나요?


  • SDS_haein
    570
    2018-08-22 16:11:21

    A페이지에서 sock.send($("#message").val()); 이부분은 아무 값이 들어가지않습니다.

    B페이지를 reload시키기위한 일종의 트릭같은거에요,,,.

  • odyssey320
    792
    2018-08-22 16:15:27

    그럼 마지막 질문.


    dataset의 데이터인 listses의 값을 불러오는 controller는 있으시죠?

    그부분좀 적어주세요.


  • SDS_haein
    570
    2018-08-22 16:28:35 작성 2018-08-22 16:29:18 수정됨

    controller는 아니고 class입니다.

    ...

    import com.spt.bbsDAO.BDAO;

    import com.spt.bbsVO.BVO;


    public class ListCmd implements Bcmd {

    //** 게시판의 리스트를 DB로 부터 가져오는 객체



    @Override

    public void service(Model model) {

    BDAO dao = new BDAO();

    ArrayList<BVO> bVOs = dao.list();

    model.addAttribute("listses",  bVOs);

    }

    }

    listses는 VO객체를 가져오는것입니다.
  • odyssey320
    792
    2018-08-22 16:39:20 작성 2018-08-22 16:41:17 수정됨

    일단 제가 처음 해결하려던 방식은,

    1. a.jsp에서 입력한 데이터를 websocket 핸들러로 보낸다.

    2. websocket 핸들러에서는 받은값을 json형태로 만든후 .toString으로 브로드캐스팅한다.

    3. b.jsp에서는 json형태의 값을 받아서 dataset에 push한다.

    4. dataset이 갱신되면 차트를 다시 그린다.

    입력 message값으로 핸들링하는 flow입니다.


    이방법은 제가 현재 상용서버에서 사용하는 방식인데 입력데이터가없으시고 그냥 신호만 받으시는거니 의미가없네요.

    해서 db데이터를 핸들링해볼까 생각했는데, 그건 너무 의미없는 작업이 많아져서 추천하고 싶지가 않네요. (dao에서 받은 값을 json타입으로 변환한후 websocket핸들러에서 브로드 캐스팅한후 onMessage에서 dataset을 새로만드는 방법)


    현재 만드신 소스는 새로고침이 가장 맞는거 같고 가장 리소스 소모가 적습니다.

    하지만 전체 화면 깜빡이는게 매우 거슬릴테니

    jquery load 형식으로 진행하는것도 나쁘지않을거같은데..어떠신지요?

    (페이지를 그리는 형식이아니라 페이지를 container에 불러와서 load시키는 방법)

  • SDS_haein
    570
    2018-08-22 16:40:59 작성 2018-08-22 16:42:31 수정됨

    아 점심이후부터 쭉 친절한설명 감사합니다. jquery load는 어떤거고 어떻게 사용하나요? 처음들어봐요

  • odyssey320
    792
    2018-08-22 16:43:45

    페이지를 ajax형태로 불러오는 방법으로 ajax page.load라고 보시면 됩니다.


    전체 깜빡임은 당연히 없고, 내가 원하는 container안에 페이지를 불러오는 방식입니다.


    아마 다른거 개발하실때도 유용하실꺼예요. 매우쉬움


    설명은 b차트 jsp 전체를 올려주시면 제가 만들어 보도록하겠습니다. 

  • SDS_haein
    570
    2018-08-22 16:52:55 작성 2018-08-22 16:53:18 수정됨

    감사합니다...저도 찾아서 해보고있겠습니다.

    b차트.jsp전체내용입니다.

    <%@ 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>
    <html>
    <head>
    <style>
    svg {
    	width: 500px;
    	height: 340px;
        border: 0px solid;
    }
    .axis {
    	font: 14px sans-serif;
    }
    .text {
        fill: white;
        font-weight:bold;
    }
    .grid line {
        stroke: lightgrey;
        stroke-opacity: 0.6;
    }
    .grid2 line {
        stroke: black;
        stroke-opacity: 0.9;
    }
    .lineChart {
        fill: none;
        stroke: red;
        stroke-width: 1.5px;
    }
    .lineChart:hover {
        stroke: black;
        stroke-width: 3px;
    }
    .toolTip {
        position: absolute;
        border: 2px solid #6799FF;
        border-radius: 4px 4px 4px 4px;
        background: rgba(253, 253, 253, 0.8);
        color : black;
        padding: 5px;
        text-align: center;
        font-size: 12px;
        min-width: 70px;
    }
    </style>
    <meta charset="EUC-KR">
    <title>Insert title here</title>
    <svg width="500" height="340"></svg>
    <!-- jQuery CDN-->
    <script src="http://code.jquery.com/jquery-latest.min.js"></script>
    
    <!-- D3 chart CDN -->
    <script src="https://d3js.org/d3.v4.min.js"></script>
    
    <!-- Web socket CDN -->
    <script src="http://cdn.sockjs.org/sockjs-0.3.4.js"></script>
    <script type="text/javascript"src="<c:url value="/resources/js/sockjs.js"/>"></script>
    <script type="text/javascript"src="<c:url value="/resources/js/sockjs.min.js"/>"></script>
    <script type="text/javascript">
    //  $(function(){
    
    var dataset = new Array();
    <c:forEach var="vo" items="${listses}" >
        var DBDataObject = {x : ${vo.bNo}, y : ${vo.bHit} }; // db data -> javascript object
        var JSONObject = JSON.stringify(DBDataObject); // javascript object -> json data
        dataset.push(DBDataObject);
    </c:forEach>
    // 	 });
    
    /*
     * web socket 통신
     */
    //웹소켓을 지정한 url로 서버 연결.
    let sock = new SockJS("<c:url value="/echo"/>");
    sock.onmessage = onMessage;
    sock.onclose = onClose;
    
    /*
    // 메시지 전송
    function sendMessage() {
    	sock.send($("#message").val());
    }
     */
    // 메시지 처리
    function onMessage(msg) {
    	 setInterval("location.reload()",1500);
        }
    
    // 서버 연결 해제
    function onClose(evt) {
    	[dataset].append("연결 끊김");
    	}
    
    
    var margin = {left: 40, top: 30, right: 10, bottom: 50};
    var svg = d3.select("svg");
    var width  = parseInt(svg.style("width"), 10)  - margin.left - margin.right;
    var height = parseInt(svg.style("height"), 10) - margin.top  - margin.bottom;
    var svgG = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    var xScale = d3.scaleBand()
    .domain(dataset.map(function(d) { return d.x;} ))
    //.range([0, width]).padding(0.2);
    .range([0, 900]).padding(0.2) //top
    var yScale = d3.scaleLinear()
    .domain([0, d3.max(dataset, function(d){ return d.y; })])
    .range([height, 0]);
    
    svgG.append("g")
    	.attr("class", "grid")
    	.attr("transform", "translate(0," + height + ")")
    	.call(d3.axisBottom(xScale)
    			.tickSize(-height)
    			)
    			.selectAll("text")
    				.attr("class", "axis")
    			    .style("text-anchor", "end")
    			    .attr("dx", "-.8em")
    			    .attr("dy", ".15em")
    			    .attr("transform", "rotate(-65)");
    
    svgG.append("g")
    	.attr("class", "grid")
    	.call(d3.axisLeft(yScale)
    	.ticks(5)
    	)
    			.selectAll("text")
    				.attr("class", "axis");
    
    
    
    var line = d3.line()
    	.x(function(d) {return xScale(d.x) +20; })
    	.y(function(d) {return yScale(d.y) ; });
    
    var lineG = svgG.append("g")
    	.selectAll("g")
    	.data([dataset])
    		.enter().append("g");
    
    
    lineG.append("path")
    	.attr("fill", "none")
    	.attr("stroke", "royalBlue")
    	.attr("stroke-width", "1.5px")
    	.attr("d", line)
    
    
    //outer dot
    lineG.selectAll("dot2")
    	.data(function(d) {return d })
    	.enter().append("circle")
    		.attr("fill", "#00008b")
    	    .attr("r", 5)
    	    .attr("cx", function(d) { return xScale(d.x) +20;})
    	    .attr("cy", function(d) { return yScale(d.y) ;})
    	    .on("mouseover", function() { tooltip.style("display", null); })
    	    .on("mouseout",  function() { tooltip.style("display", "none"); })
    	    .on("mousemove", function(d) {
    	        tooltip.style("left", (d3.event.pageX+10)+"px");
    	        tooltip.style("top",  (d3.event.pageY+0)+"px");
    	        tooltip.html("<b>" + "No. " + d.x + "<br/>" + "Hit : " + d.y);
    	    });
    
    //inner dot
    lineG.selectAll("dot1")
    	.data(function(d) {return d })
    	.enter().append("circle")
    		.attr("fill", "#fff")
    	    .attr("r", 3)
    	    .attr("cx", function(d) { return xScale(d.x) +20;})
    	    .attr("cy", function(d) { return yScale(d.y) ;})
    	    .on("mouseover", function() { tooltip.style("display", null); })
    	    .on("mouseout",  function() { tooltip.style("display", "none"); })
    	    .on("mousemove", function(d) {
    	        tooltip.style("left", (d3.event.pageX+10)+"px");
    	        tooltip.style("top",  (d3.event.pageY+0)+"px");
    	        tooltip.html("<b>" + "No. " + d.x + "<br/>" + "Hit : " + d.y);
    	    });
    
    var tooltip = d3.select("body")
    	.append("div")
    	.attr("class", "toolTip")
    	.style("display", "none");
    
    </script>
    </body>
    </html>


  • SDS_haein
    570
    2018-08-22 17:05:35 작성 2018-08-22 17:06:31 수정됨
    처음부터 구현하실려면 복잡할 수 있습니다. 비슷하게라도 간단한 구현코드 알려주셔도 되요 ㅠㅠㅠㅠ
  • odyssey320
    792
    2018-08-22 17:11:57 작성 2018-08-22 17:21:36 수정됨


    <%@ 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>
    <html>
    <head>
    <style>
    svg {
    	width: 500px;
    	height: 340px;
        border: 0px solid;
    }
    .axis {
    	font: 14px sans-serif;
    }
    .text {
        fill: white;
        font-weight:bold;
    }
    .grid line {
        stroke: lightgrey;
        stroke-opacity: 0.6;
    }
    .grid2 line {
        stroke: black;
        stroke-opacity: 0.9;
    }
    .lineChart {
        fill: none;
        stroke: red;
        stroke-width: 1.5px;
    }
    .lineChart:hover {
        stroke: black;
        stroke-width: 3px;
    }
    .toolTip {
        position: absolute;
        border: 2px solid #6799FF;
        border-radius: 4px 4px 4px 4px;
        background: rgba(253, 253, 253, 0.8);
        color : black;
        padding: 5px;
        text-align: center;
        font-size: 12px;
        min-width: 70px;
    }
    </style>
    <meta charset="EUC-KR">
    <title>Insert title here</title>
    <body>
    <div id="chart-container"></div>
    <!-- <svg width="500" height="340"></svg> -->
    <!-- jQuery CDN-->
    <script src="http://code.jquery.com/jquery-latest.min.js"></script>
    
    <!-- D3 chart CDN -->
    <script src="https://d3js.org/d3.v4.min.js"></script>
    
    <!-- Web socket CDN -->
    <script src="http://cdn.sockjs.org/sockjs-0.3.4.js"></script>
    <script type="text/javascript"src="<c:url value="/resources/js/sockjs.js"/>"></script>
    <script type="text/javascript"src="<c:url value="/resources/js/sockjs.min.js"/>"></script>
    <script type="text/javascript">
    //  $(function(){
    
    var dataset = new Array();
    <c:forEach var="vo" items="${listses}" >
        var DBDataObject = {x : ${vo.bNo}, y : ${vo.bHit} }; // db data -> javascript object
        var JSONObject = JSON.stringify(DBDataObject); // javascript object -> json data
        dataset.push(DBDataObject);
    </c:forEach>
    // 	 });
    
    /*
     * web socket 통신
     */
    //웹소켓을 지정한 url로 서버 연결.
    let sock = new SockJS("<c:url value="/echo"/>");
    sock.onmessage = onMessage;
    sock.onclose = onClose;
    
    /*
    // 메시지 전송
    function sendMessage() {
    	sock.send($("#message").val());
    }
     */
    // 메시지 처리
    function onMessage(msg) {
    	 //setInterval("location.reload()",1500);
    	 $("#chart-container").load( "/callChart.do", function() {});
    }
    
    // 서버 연결 해제
    function onClose(evt) {
    	[dataset].append("연결 끊김");
    }
    
    
    var margin = {left: 40, top: 30, right: 10, bottom: 50};
    var svg = d3.select('#chart-container')
    		.append('svg')
    		.attr('width', 500)
    		.attr('height', 340);
    var width  = parseInt(svg.style("width"), 10)  - margin.left - margin.right;
    var height = parseInt(svg.style("height"), 10) - margin.top  - margin.bottom;
    var svgG = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    var xScale = d3.scaleBand()
    .domain(dataset.map(function(d) { return d.x;} ))
    //.range([0, width]).padding(0.2);
    .range([0, 900]).padding(0.2) //top
    var yScale = d3.scaleLinear()
    .domain([0, d3.max(dataset, function(d){ return d.y; })])
    .range([height, 0]);
    
    svgG.append("g")
    	.attr("class", "grid")
    	.attr("transform", "translate(0," + height + ")")
    	.call(d3.axisBottom(xScale)
    			.tickSize(-height)
    			)
    			.selectAll("text")
    				.attr("class", "axis")
    			    .style("text-anchor", "end")
    			    .attr("dx", "-.8em")
    			    .attr("dy", ".15em")
    			    .attr("transform", "rotate(-65)");
    
    svgG.append("g")
    	.attr("class", "grid")
    	.call(d3.axisLeft(yScale)
    	.ticks(5)
    	)
    			.selectAll("text")
    				.attr("class", "axis");
    
    
    
    var line = d3.line()
    	.x(function(d) {return xScale(d.x) +20; })
    	.y(function(d) {return yScale(d.y) ; });
    
    var lineG = svgG.append("g")
    	.selectAll("g")
    	.data([dataset])
    		.enter().append("g");
    
    
    lineG.append("path")
    	.attr("fill", "none")
    	.attr("stroke", "royalBlue")
    	.attr("stroke-width", "1.5px")
    	.attr("d", line)
    
    
    //outer dot
    lineG.selectAll("dot2")
    	.data(function(d) {return d })
    	.enter().append("circle")
    		.attr("fill", "#00008b")
    	    .attr("r", 5)
    	    .attr("cx", function(d) { return xScale(d.x) +20;})
    	    .attr("cy", function(d) { return yScale(d.y) ;})
    	    .on("mouseover", function() { tooltip.style("display", null); })
    	    .on("mouseout",  function() { tooltip.style("display", "none"); })
    	    .on("mousemove", function(d) {
    	        tooltip.style("left", (d3.event.pageX+10)+"px");
    	        tooltip.style("top",  (d3.event.pageY+0)+"px");
    	        tooltip.html("<b>" + "No. " + d.x + "<br/>" + "Hit : " + d.y);
    	    });
    
    //inner dot
    lineG.selectAll("dot1")
    	.data(function(d) {return d })
    	.enter().append("circle")
    		.attr("fill", "#fff")
    	    .attr("r", 3)
    	    .attr("cx", function(d) { return xScale(d.x) +20;})
    	    .attr("cy", function(d) { return yScale(d.y) ;})
    	    .on("mouseover", function() { tooltip.style("display", null); })
    	    .on("mouseout",  function() { tooltip.style("display", "none"); })
    	    .on("mousemove", function(d) {
    	        tooltip.style("left", (d3.event.pageX+10)+"px");
    	        tooltip.style("top",  (d3.event.pageY+0)+"px");
    	        tooltip.html("<b>" + "No. " + d.x + "<br/>" + "Hit : " + d.y);
    	    });
    
    var tooltip = d3.select("body")
    	.append("div")
    	.attr("class", "toolTip")
    	.style("display", "none");
    
    </script>
    </body>
    </head>
    </html>


    전체를 붙여넣기하시고, onMessage 함수안에 /callChart.do 이부분에 이차트를 호출하는 url을 넣으시면 됩니다. 테스트해보세요.

  • SDS_haein
    570
    2018-08-22 17:22:41

    이 차트페이지를 호출하는 url은 없어요 ㅜㅜ 화면이 두개이고 다른화면에 이 차트페이지가 항상띄워져있어서 ㅠㅠ

  • odyssey320
    792
    2018-08-22 17:26:48

    web페이지인데 url이 없어요?servlet으로 호출하는거 아닌가요?

  • odyssey320
    792
    2018-08-22 17:31:06

    흠 잘모르시겠으면 그냥 /callChart.do 이부분에 #target이라고 넣으세요.

  • SDS_haein
    570
    2018-08-22 17:32:08

    아 그런뜻이였구나..

    servlet으로 처음에 호출하는건 맞는데,

    그이후엔 계속 띄워져있어서 다른 web jsp페이지에서 호출되지는 않아요


  • SDS_haein
    570
    2018-08-22 17:35:43

    헐미친...됐어요 ㅠㅠㅠ 감사해요 ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ 근데 너무빨리갱신되서 차트페이지에서 못따라오네요

    thread를 걸어줘야 할것같아요 

    본질적인 해결은 덕분에 잘 해결됬씁니다..ㅠㅠㅠㅠㅠ 카톡아이디 댓글로 남겨주시고 지워주세요 카톡으로 기프티콘 드릴게요 ㅠㅠㅠㅠㅠㅠㅠㅠㅠ

  • odyssey320
    792
    2018-08-22 17:36:47

    호출은 기존에는 

    location.reload() 가 했잖아요.

    그부분을 

    $("#chart-container").load( "#target", function() {});

    이놈이 하게 바꾼거예요.

  • odyssey320
    792
    2018-08-22 17:37:50

    thread는 자바에서 쓰는거고 setTimeout으로 주세요.

  • odyssey320
    792
    2018-08-22 17:39:03 작성 2018-08-22 17:40:30 수정됨

    해결 되셨나요?

    jquery.load는 인자도 들고다닐수 있어서 유용하게 쓰일대가 많아요.

    직접적인 페이지 파싱이 없기때문에 기존에 페이지에서도 사용하기 쉽고요..

    뭐 얻어먹을려고한건아니지만. id는 odyssey320입니다.ㅋㅋㅋㅋ

  • SDS_haein
    570
    2018-08-22 17:52:50

    감사합니다 :)

  • SDS_haein
    570
    2018-08-23 09:54:33

    마지막으로... setTimeout 제가 코딩을 잘못짠건가요?


    // 메시지 처리
    function onMessage(msg) {
    	 //setInterval("location.reload()",1500);
    	 var tick = setTimeout(function() {
    	 $("#chart-container").load( "#target", function() {});
    	 }, 1000);
    
    }
    
    // 서버 연결 해제
    function onClose(evt) {
    
    	 clearTimeout(tick);
    	[dataset].append("연결 끊김");
    }
    


    setTimeout이 가끔 안멈추고 계속 돌때가 있어서 서버가 다운되는 것 같아요.

    onMessage()안에 마지막에 clearTimeout을 집어 넣을때는 갱신이 안되더라구요ㅜㅜ

  • odyssey320
    792
    2018-08-23 10:34:01

    정확한 문제를 잘 모르겠는데..혹시 1초내에 여러번 호출되서 생기는 문제인건가요?

    만약 그런거면 onMessage에서 processing 중에는 다시 호출못하도록 flag를 하나 두시면 될거같은데..

    (물론 이경우 1초내에 10번의 호출이 와도 가장먼저 호출된거를 제외한 나머지 호출은 처리안하지만요)




  • SDS_haein
    570
    2018-08-23 12:48:01 작성 2018-08-23 12:49:22 수정됨

    헐 문제의 원인을 정확히 알고계시네요...

    네 데이터갱신은 1번만 잘되는것같은데..Processing이 콘솔에 1초내에 10번정도 호출되는것 같아요.
    말씀하신데로 flag 사용해보겠습니다. 감사합니다 ㅜㅜ

  • 로그인을 하시면 답변을 등록할 수 있습니다.