Backends Frontends Javascript PHP

코멧(Comet) #2 – Ajax 폴링(Ajax polling) 채팅방 예제로 배우기

코멧을 구현하는 첫 번째 방법은 Ajax 폴링(Ajax Polling)이라고 하는 기법입니다. 이 기법은 모든 코멧 방법론 중에서 가장 직관적이고 구현이 간단하여, 코멧 방법론에 입문할 때에 처음 만나게 되는 기법입니다. 기본적인 아이디어는 일정시간마다 Ajax 통신을 하여 서버의 상태를 가져오는 것이라고 요약할 수 있습니다.

이 포스팅에서는 자바스크립트와 더불어 PHP, MySQL로 구현한 간단한 채팅방 예제를 통해 Ajax 폴링을 구현하는 방법을 알아봅니다.

 

Ajax 폴링 채팅방 예제코드 (다운로드)

금번에 소개하는 예제는 Ajax 폴링 기법으로 최소한의 기능만을 갖춘 채팅방을 구현한 것입니다. zip 파일 1개로 압축되어 있으며, 아래와 같이 6개의 파일이 있습니다.

  • index.html : 채팅방 메인 페이지
  • proc.php : GET 파라메터로 주어진 날짜 이후의 채팅내용을 가져와 JSON 포맷으로 출력하는 페이지
  • write.php : 닉네임과 채팅내용을 받아 데이터베이스에 입력하는 페이지
  • chat.js : 자바스크립트 파일
  • chat.css : 스타일시트 파일
  • chat.sql : 데이터베이스 테이블을 생성하기 위한 sql문 (MySQL 기준)

본 예제를 여러분의 서버에 업로드하여 실행하실 때에는, 먼저 MySQL 상에서 chat.sql 파일 안의 sql문을 실행하여 DB 테이블을 생성하셔야 합니다. 또한 proc.php와 write.php 파일에는 DB 연결하는 코드가 있으므로, 이 부분을 여러분이 사용하는 MySQL의 계정과 비밀번호, 데이터베이스 이름으로 바꾸셔야 합니다.

Ajax Polling 채팅방 예제

제대로 업로드와 세팅을 마쳤다면, 이제 서로 다른 여러 개의 웹 브라우저로 index.php를 호출해보시면 위처럼 채팅방이 실행되는 모습을 볼 수 있습니다.

 

자바스크립트 예제코드 – chat.js

성공적으로 예제를 실행할 수 있었다면 이제 자바크스립트 코드를 살펴볼 차례입니다. 아래 예제코드에서는 좀더 분명한 설명을 위해, 여러분들이 다운로드 받은 소스코드 중 일부가 생략되어 있습니다.

var chatManager = new function(){
	var interval	= 500;
	var xmlHttp		= new XMLHttpRequest();
	var finalDate	= '';

	// Ajax Setting
	xmlHttp.onreadystatechange = function()
	{
		if (xmlHttp.readyState == 4 && xmlHttp.status == 200)
		{
			// JSON 포맷으로 Parsing
			res = JSON.parse(xmlHttp.responseText);
			finalDate = res.date;
			
			// 채팅내용 보여주기
			chatManager.show(res.data);
		}
	}

	// 채팅내용 가져오기
	this.proc = function()
	{
		// Ajax 통신
		xmlHttp.open("GET", "proc.php?date=" + finalDate, true);
		xmlHttp.send();
	}

	// 채팅내용 보여주기
	this.show = function(data)
	{
		var o = document.getElementById('list');
		var dt, dd;

		// 채팅내용 추가
		for(var i=0; i<data.length; i++)
		{
			dt = document.createElement('dt');
			dt.appendChild(document.createTextNode(data[i].name));
			o.appendChild(dt);

			dd = document.createElement('dd');
			dd.appendChild(document.createTextNode(data[i].msg));
			o.appendChild(dd);
		}
	}

	// interval에서 지정한 시간마다 실행
	setInterval(this.proc, interval);
}

위 코드가 생성하고 있는 chatManager라는 객체는 먼저 XMLHttpRequest 객체를 하나 가지고 있습니다. 이 객체를 통해 Ajax 통신이 완료되었을 때 수행할 로직은 7번줄부터 정의되어 있는데, responseText를 JSON으로 파싱하여, 날짜는 변수에 저장하고, 채팅내용은 show() 함수에 인자로 넘겨주는 매우 간단한 형태입니다. 이렇게 호출된 show() 함수는 반복문으로 인자로 받은 채팅내용을 화면에 HTML 코드로 표시해줍니다.

실제로 Ajax 통신을 시작하는 함수는 21번줄부터 시작하는 proc()으로, 내용을 살펴보면 proc.php라는 파일에 변수에 저장되어 있던 날짜를 인자로 담아 HTTP 요청을 보내는 것을 볼 수 있습니다. 이 PHP 파일에는 데이터베이스에서 인자로 받은 날짜 이후에 입력된 채팅내용들과 HTTP 요청이 들어온 날짜를 JSON 포맷으로 출력해주는 코드가 담겨 있습니다. 이 proc() 함수는 48번줄의 setInterval()에 의해 500ms에 한 번씩 실행됩니다.

이상의 내용을 종합하면, 우리는 이 코드를 실행했을 때 어떻게 작동하는지를 알 수 있습니다. 위 코드는 500ms마다 Ajax 통신을 하여 데이터베이스로부터 JSON 포맷으로 된 채팅내용을 가져와 HTML 코드로 화면에 표시합니다. 이 과정에서 마지막으로 채팅내용을 가져온 날짜를 매번 갱신하기 때문에, 한 번 가져왔던 채팅내용은 다음 Ajax 요청에서는 가져오지 않게 됩니다. 일정시간마다 Ajax 통신으로 서버의 상태를 가져오는 Ajax 폴링 기법의 원리를 여기서 확인할 수 있습니다.

 

Ajax 폴링의 장단점

위 예제코드에서도 확인한 바와 같이, Ajax 폴링은 원리가 단순한만큼 간결한 소스코드 만으로도 손쉽게 구현할 수 있습니다. 생산성과 유지보수의 수월성 측면에서 Ajax 폴링은 확실한 이점을 가지고 있습니다. 그러나 단순한 구현방법에는 그만큼의 결점이 있는 법입니다.  Ajax 폴링의 한계는 2가지 정도로 정리할 수 있습니다.

첫 번째 한계는 HTTP 통신의 빈도가 비교적 높다는 것입니다. 이 때문에 실시간성이 중요한 대형 서비스에서 Ajax 폴링을 도입하기는 어렵습니다. 위 예제코드의 경우, HTTP 통신이 이루어지는 빈도는 1초에 2회입니다. 만약 위 예제코드로 서비스하는 채팅방에 100명이 접속한다면, 서버는 1초에 200회나 되는 접속량을 감당해야 할 것입니다.

두 번째 한계는 Ajax 통신에서 응답이 지연될 경우에 위험한 상황이 발생할 가능성이 있다는 것입니다. 이를테면 위 예제코드에서 서버나 데이터베이스에 부하가 높아져 proc.php 파일의 응답시간이 500ms를 넘으면 어떻게 될까요. 아직 이전의 Ajax 통신이 끝나지 않았음에도 다음 Ajax 통신이 중첩되어 실행될 우려가 있습니다. 이렇게 중첩되어 실행된 Ajax 통신은 또다시 지연을 가중시키고, 그렇게 느려진 와중에 또다시 Ajax 통신이 시작되고, 또다시 지연이 가중되는 악순환이 벌어집니다. 여러분께서 다운로드 받은 예제파일에는 이러한 문제를 막고자 Ajax 통신의 중복실행을 방지하는 플래그가 들어 있습니다.

 

Ajax 폴링을 도입하기에 적합한 상황

그렇다고 Ajax 폴링이 실무에서 전혀 쓸 수 없는 애물단지는 아닙니다. Ajax 폴링은 HTTP 통신간격을 충분히 두어도 되는 서비스에서는 유용하게 사용할 수 있습니다.

이를테면 스포츠 경기 문자중계의 경우, 10~20초에 한 번 정도만 업데이트 해주어도 비교적 만족스러운 서비스의 질을 유지할 수 있습니다. 이 정도의 시간간격이라면 서버가 감당해야 하는 접속량도 줄어들고, 일시적으로 응답이 지연되더라도 위험한 상황에 이를 가능성도 줄어들 것입니다.

46 thoughts on “코멧(Comet) #2 – Ajax 폴링(Ajax polling) 채팅방 예제로 배우기”

    1. 방문과 덧글 감사합니다. 롱 폴링(Long Polling)에 관한 3번째 포스팅도 최대한 빠르게 준비하겠습니다.

  1. 소중한 지식을 공유해 주셔서 감사합니다.
    혹시~ 작성되는 글이 지금은 위로 쌓이지만 아래쪽으로 내려가면서 정렬되게 하려면 어떻게 해야되는지 정중히 여쭈어 봅니다.

    1. 해외에 있었던터라 답이 많이 늦어 죄송합니다. 예제파일을 실행해보시면, 노스텔지어님께서 원하시는대로 위에서 아래쪽으로 시간순 정렬이 되면서 채팅내용이 표시됨을 확인할 수 있습니다. 시간역순 정렬을 원하신다면 appendChild() 대신 insertBefore()를 활용하면 되겠습니다.

    1. 저야말로 방문해주시고 흔적 남겨주셔서 감사합니다. 많은 도움이 되셨기를 바라겠습니다.

  2. socket.io 를 이용한 채팅 관련 포스팅도 해주실수있을까 싶네요. 이번에도 좋은 글 잘 읽었습니다^^

    1. 심재문님 반갑습니다. IE 버전을 알지 못하고 콘솔창을 보지 않은 상태에서 정확히 진단하기는 어렵지만, 가장 쉽게 의심해볼 수 있는 부분은 12번줄의 JSON 객체입니다. JSON 객체는 IE7 이하에서 지원하지 않는 것으로 알려져 있고, IE8에서도 작동하지 않았다고 주장하는 사례도 있습니다. JSON 객체를 대체하는 방법에 대해서는 json.org 사이트에서도 다루고 있습니다. 다음 링크를 참고해보시면 좋겠습니다: http://www.json.org/js.html

  3. 군인이라서 예제를 그대로 사용못하고

    예제를 다운받아서 제 테스트용 PC에 손수 타이핑하면서 전부 적었는데

    예제안에 chat.js 에서 xmlHttpWrite.send(param.join(‘&’)); 부분에 send가 잘 안되는거 같습니다.

    DB에 안들어갑니다. 그리고 저 지점에서 디버깅을 해보니 readytState = 1, status = 0 이뜹니다.

    저 지점에서 readytState = 1, status = 0 이 나오는게 맞는건가요?

    아님 다른부분이 틀려서 안되는 걸까요?

    1. 군복무로 노고가 많으십니다. 소스코드를 볼 수가 없어 정확한 진단은 어렵습니다만, 일반적으로 readyState 프로퍼티가 항상 1로 나오는 경우에는 디버깅 위치가 잘못된 경우가 많습니다. 제 예측이 맞다면, 아래와 같이 send() 메소드를 호출한 직후에 디버깅을 하셨을 가능성이 높습니다.

      xmlHttpWrite.send(param.join(‘&’));
      alert(xmlHttpWrite.readyState + ” ” + xmlHttpWrite.status);

      이것은 잘못된 것입니다. readyState 프로퍼티의 값은 최초에 0에서 시작하여, Ajax 통신이 진행되면서 1씩 올라가 최종적으로 4까지 변하게 됩니다. 하지만 위처럼 디버깅을 하면 send() 메소드를 호출한 직후의 readyState 값 밖에는 디버깅을 할 수 없습니다. 정확한 디버깅 방법은 아래와 같이 onreadystatechange() 메소드 안에서 디버깅을 하는 것입니다.

      xmlHttp.onreadystatechange = function()
      {
        alert(xmlHttpWrite.readyState + ” ” + xmlHttpWrite.status);
        // 그 외의 코드는 이 아래에 놓습니다.
      }

      onreadystatechange()는 readyState 값이 변할 때마다 호출되는 메소드입니다. 따라서 위와 같이 디버깅을 했을 때 Ajax 통신이 정확하게 이루어졌다면, 경고창이 4번 표시가 되며, 표시될 때마다 readyState이 1에서 4로 변화하는 모습을 볼 수 있습니다. 아울러 status 프로퍼티는 readyState가 4가 된 순간에, 200(정상) 혹은 404(페이지 없음) 중 하나로 변하는 것을 확인하게 될 것입니다.

      위 방법을 이용해서 다시 한 번 디버깅을 진행해보시고, 제가 잘못 예측했거나 후속질문이 있으시면 덧글 남겨주세요.

  4. 굉장히 수고가 많으십니다. 공유 해 주신것 또한 매우 감사하게 생각합니다.

    보고 많은 공부가 되었습니다. 정말 감사합니다.

    1. 방문해주시고 댓글까지 남겨주셔서 감사합니다. Hidden iframe이나 롱폴링에 대해서도 준비를 해야하는데, 매번 작성을 못하고 있어서 많은 분들께 송구한 마음을 가지고 있습니다. 앞으로도 좋은 글로 찾아 뵙겠습니다.

      1. 교육내용은 잘 읽어보았습니다. 감사합니다

        혹시 리눅스에서 코멧을이용하여 클라이언트들에게 일방적으로

        파일을 푸쉬가 가능하게끔 하려면 어떤 문서를 참조하면 될까요?

        (업데이트서버를 구현해보고자 합니다 , client들의 업데이트여부를 DB로 파악하여
        업데이트를 하지않은 pc들에게 일방적으로 파일 전송이 가능하게끔…)

        1. 질문 감사드립니다. 먼저 말씀하신 내용은 서버푸시에 관한 것인데, 일반적으로는 HTTP 통신으로 구현할 수 없습니다. 이전 글(http://dev.epiloum.net/790)에도 다루었지만, 웹 환경의 기반이 되는 HTTP 통신은 클라이언트의 요청에 대해서 서버가 응답하는 형태로 작동하기 때문입니다. 클라이언트의 요청이 없이 서버가 단독으로 푸시를 할 수는 없습니다.

          가장 이상적인 방법은 서버 프로그램을 직접 작성하는 것입니다. 최근에는 Node.js와 같이 서버 프로그램을 쉽게 작성할 수 있는 도구가 많이 있습니다. 구현시 프로토콜은 자체적으로 정할 수도 있고, FTP와 같이 기존에 있는 프로토콜을 이용할 수도 있겠지요.

          끝내 아파치 서버와 HTTP 통신을 포기할 수 없다면, 생각을 조금 바꾸셔야 할 것 같습니다. 바로 클라이언트들이 충분히 짧은 시간간격으로 업데이트 서버에 HTTP 통신을 하는 것입니다. 업데이트 서버는 HTTP 통신을 받으면, 업데이트 여부를 DB에서 확인한 후에 필요한 파일을 전송하면 되고, 업데이트할 파일이 없다면 미리 정해진 행동을 취하게 하면 될 것입니다.

  5. 좋은 글 잘봤습니다^^ 제가 꼭 필요한 내용이었고 많은 도움이 되었습니다.
    질문이 있어서 댓글을 달게되었는데요
    db값을 ajax로 일정한 주기로 값을 가져오면서 row data bound를 할때 그 값을 체크해서
    1인경우 이미지를 바꾸고 알림을 주고 싶습니다.
    그런데 이미지는 바뀌는데 알림은 처음 로드시에 값이 1인경우에만 팝업이 뜨더라구요. 그뒤로 바뀌는 값들에 대해서는 알림팝업이 뜨지않습니다.
    팝업은 자바스크립트로 주었구요.. 좋은 방법이 없을까요ㅠㅠ

    1. 안녕하세요, 글에 관심을 가져주시고 질문을 남겨주셔서 감사합니다. 소스코드를 직접 볼 수 없어 진단하기가 어렵습니다만, 이미지를 변경하는 코드와 Alert를 띄우는 코드가 같은 함수 안에 있다만 사실상 위와 같이 작동할 가능성은 대단히 낮아 보입니다.

      만약 두 코드가 같은 함수 안에 있음에도 위와 같이 작동한다면, 브라우저가 Alert를 막았을 가능성이 있습니다. (예를 들어 크롬의 경우, 자바스크립트로 표시한 Alert창에 “이 페이지가 추가적인 대화를 생성하지 않도록 차단합니다”라는 체크박스가 있어 이를 체크하면 더이상 해당 페이지에서 Alert가 표시되지 않습니다.) 이 경우가 의심되는 경우에는 브라우저를 변경하여 테스트해보시면 좋을 것 같습니다.

      만약 두 코드가 서로 다른 함수 안에 있을 때에는 경우의 수가 많습니다. 그러나 예상할 수 있는 가장 대표적인 실수는 Closure 문제, 즉 이벤트 발생의 시차로 인해 변수가 다른 값으로 덮어쓰여지는 문제를 예상할 수 있습니다. row data bound 할 때의 값을 전역변수에 넣어 참조하게 구현하셨거나 함수의 인자로 row data bound 할 때의 값을 넘기고 있다면, 실제로 각 코드가 실행될 때 변수에 무엇이 들어있는지 확인해보시는 것이 좋을 것 같습니다.

  6. 감사합니다 덕분에 Comet에 관하여 많은 지식을 배워 가는것 같습니다.
    다른 포스팅도 잘읽어보고 새로 올라올 글들도 기대하겠습니다!

    1. cokey님, 안녕하세요. 좋은 포스팅으로 계속 찾아 뵈어야 하는데, 업무시간 외에 시간을 할애하기가 참 어렵습니다. 관심 가져주시는 따뜻한 덧글에 좀더 힘을 내야겠다는 다짐 해봅니다. 감사합니다.

  7. 포스팅 잘 읽었습니다~
    궁금한게 있어서 질문 하나 드립니다.. 현재 redis에 있는 데이터를 실시간으로 보여주는 페이지를 간단하게 만들고 있는데 폴링 방식이 맞는 건지 .. 궁금하네요 .. 1초에 한 번씩 redis에 데이터를 가져올 생각인데 데이터양이 그렇게 많지 않고 웹페이지 사용자도 3명정도입니다. 웹 소켓도 찾아 봤는데 그것보단 폴링 방식이 맞지 않을까 싶은데 .. 조언 부탁드리겠습니다.

    1. mercy님, 반갑습니다. 추측하기로는 아마도 Redis에 저장된 값을 모니터링하는 도구를 작성하려고 하시는 것 같습니다.

      말씀하신 내용을 폴링으로 구현할 때 가장 중요하게 생각해보아야 할 부분은, 통신이 1초에 한 번 일어난다는 것입니다. 이것은 곧 어떠한 이유로 통신이 1초 이상 지연되는 상황이 발생하고 이것이 지속되면, connection이 쌓일 염려가 있음을 의미합니다. 만약 구현환경을 검토해보셨을 때, 이런 1초 이상의 지연이 자주 발생한다면 폴링은 적합한 구현방식이 아닙니다. 그러나 통상적인 상황에서는 통신이 1초 미만에 종료된다고 확신할 수 있고, 1초를 넘기는 상황은 (네트워크 부하 등의) 이상상황이며 빈번한 것이 아니라고 한다면, 폴링은 손쉽게 구현할 수 있고 유지보수가 쉬운 좋은 선택일 것입니다. 중복통신을 방지하기 위한 약간의 예외처리는 필요하겠습니다만.

      다만 그럼에도 불구하고 여전히 폴링이 리소스 소모가 많은 방식임은 분명합니다. 만약에 Redis에 저장된 데이터의 변경이 빈번하지 않다면, 폴링의 통신간격을 좀더 늘리시거나 롱폴링의 도입을 검토해보시는 것을 권해드립니다.

      한편, Web Socket을 이용한 구현은 제가 Redis를 현업에서 사용해보지 못해서 정확한 답을 드리기가 어렵습니다. 일단 command를 소켓 통신으로 Redis에 직접 전달할 수 있다면 검토해볼 수 있는 구현방식이기는 합니다. 다만 대부분의 DB들과 같이 소켓 통신으로 바이너리 데이터를 보내야 하는 경우에는, 아마 Web Socket으로는 제약되는 부분이 많을 것입니다. Webdis를 비롯해 HTTP 통신으로 Redis의 command를 전달할 수 있는 API도 있는 모양입니다만, 이런 API를 쓰려고 마음먹었다면 이미 폴링이나 롱폴링으로 손쉬운 구현이 가능하기 때문에 Web Socket을 고수할 필요가 사라질 것입니다.

  8. 게시글이 새로 업데이트 되면 알람을 해주는 기능을 구현하려고 찾고 있던 정보였는데,
    좋은 정보 잘 봤습니다!!!
    추가로 읽던 중에 질문이 있어, 댓글로 질문 남겨 봅니다.
    polling과 long polling 은 단순 통신 방식이고,
    웹의 특징인, 수동적인(요청에 의해 값을 전달 받는 행위) 방식을 능동적으로 바꾸기 위해 사용하는 통신 방법으로 이해를 했습니다.
    그리고 polling과 long polling을 ajax기술을 이용해서 구현 하셨는데, ajax 말고 다르게 구현 가능한 방법이 있는지 궁금합니다.
    또한 위 방법도 어쨋든 클라이언트 쪽에서 정보를 요청하는 방식인데, 서버에서 변경된 정보를 체크하고 클라이언트쪽으로 알려주는 방법도 있는지 궁급합니다.

    다시 한번 좋은 정보 감사 합니다!

    1. 황세환님, 반갑습니다. 남겨주신 질문에 대해서 답을 드리면 아래와 같습니다.

      먼저 코멧의 구현방법에는 Ajax를 이용한 2가지 이외에도 <iframe> 태그를 이용하는 “Hidden IFrame Technique”이라는 기법이 있습니다. 이것은 HTTP 응답을 부분적으로 조금씩 쪼개어 보낼 수 있는 Chunked Response를 이용하여서, <iframe>에서 부른 웹페이지에서 자바스크립트를 쪼개어 보내는 방법입니다. 하지만 말씀하신 대로 역시 이 또한 HTTP의 특성으로 인해 클라이언트의 요청에 기반한 방법일 따름입니다.

      만약 서버에서 먼저 클라이언트에 능동적으로 메시지를 보내는 진정한 의미의 Server Push를 기대하신다면, HTML5의 Web Socket을 고려해보시면 좋을 것 같습니다. 과거에는 브라우저 호환 문제로 기피되던 방법이지만, 현재는 Internet Explorer 10 미만의 버전을 제외하면 거의 모든 브라우저에서 Web Socket을 지원합니다.

      1. 답변 감사합니다!
        혹시 Web Socket에 관한 참고할 만한 자료가 있으면, 추천 부탁드립니다.
        쌀쌀한 날씨 감기 조심하세요.
        감사합니다.

        1. https://flaviocopes.com/websockets/
          서버를 어떤 언어로 구성하느냐에 따라 달라지겠지만, Node.js로 구현하는 경우가 제 경우에는 가장 깔끔할 것 같습니다. 이 경우에는 위 URL을 참고하시면 도움이 되실 것입니다. PHP로 서버를 구현하는 경우에는 socket 관련 함수로 직접 데몬을 구현하고 쉘에서 실행해야 하므로 추천드리지는 않습니다. 그 외에 JSP의 경우에는 확인해보지는 못했지만 톰캣이 이를 지원하고, 처음 톰캣을 설치하면 WebSocket 서버구현 예제코드가 기본으로 포함되어 있다는 것으로 들었습니다. 참고하셨으면 합니다.

    1. 이지은님, 안녕하세요.

      말씀하신 내용 만으로 원인을 찾기는 어렵지만, 혹시 소스코드를 다운로드 하신 후에 수정이 전혀 없이 실행해보신 것은 아니신지요. 본문에서 말씀드린 것과 같이, 다운로드 하신 소스코드는 sql문을 실행하여 DB 테이블을 생성하고 mysqli 객체 생성시에 계정명과 비밀번호, 데이터베이스명을 입력해야 합니다.

      혹시나 위 과정을 이미 진행하셨다면, 개발자 도구 등을 이용하여 Ajax 통신시 응답받은 내용을 확인하여 진단할 필요가 있을 것 같습니다.

  9. 좋은 글 감사합니다.
    한가지 질문이 있는데요 혹시 저처럼 DB 가 로컬에 있는것이 아니라 ip 위에 있으면 저 위의 mysql 을 어떻게 설정해주어야 하나요?

    1. macqueen0987님, 안녕하세요. 먼저 관심 가져주시고 질문 남겨주셔서 감사합니다. 웹페이지가 위치한 서버와 DB 서버가 분리되어 있어, 서로 다른 IP를 가진 상황에 대해서 여쭤보아 주셨습니다.

      방법은 매우 간단합니다. 예제코드의 proc.php에서 2번째 줄, mysqli 객체의 생성자에서 첫 번째 인자를 localhost 대신에 DB 서버의 아이피를 입력하면 됩니다. 예컨대 DB 서버의 아이피가 1.2.3.4라면 아래와 같은 코드가 될 것입니다.
      $db = new mysqli(‘1.2.3.4’, ‘****’, ‘****’, ‘****’);

      만약 위와 같이 아이피 변경으로도 DB 접속이 제대로 되지 않는다면, 대게는 해당 아이피에서의 원격접속이 막혀있는 경우입니다. 방화벽 설정을 확인하여 외부에서 MySQL 포트(대게는 3306)로 접속이 가능하신지 먼저 확인하시고, 이어서 MySQL의 해당 사용자 계정의 host 접속권한을 확인해보는 순서로 점검해보시면 되겠습니다.

      1. 감사합니다 덕분에 문제가 해결되었습니다. 또 다른 궁금증이 있어서 여쭈어 봅니다. 지금 제가 하려는 것이 실시간으로 DB에 저장되는 어떤 물체의 x,y 좌표를 받아서 웹페이지에서 맵의 사진 위에 사각형으로 그리려고 하고 있는데 이를 위해서는 chat.js 파일에 대해 더 알 필요가 있나요?

        1. macqueen0987님, 안녕하세요. 이전에 드린 답변이 도움이 되었다니 다행입니다. 순수하게 기능구현에 초점을 맞춘다면, 제공해드린 예제코드에서 2가지 정도만 수정을 하면 예제코드에 대해서 깊게 이해하지 않으시고도 기능을 구현하실 수 있습니다.

          1. 먼저 proc.php가 첫 번째 수정 포인트입니다. 이곳에서 DB에서 가져온 물체의 좌표를 JSON 형식으로 출력합니다.
          2. 다음으로 자바스크립트에서는 chatManager.show() 함수 내부의 로직이 두 번째 수정 포인트입니다. 이곳에서 전달받은 좌표에 맞추어 지도 위에 사각형을 그리는 로직을 작성하시면 되겠습니다.

          다만 chat.js 소스코드를 완전하게 독해하기를 원하신다면 Ajax 통신에 대하여 살펴보시면 도움이 되실 것입니다.

          1. 염치 불구하고 다시 한번 하지 못하는 부분이 있어서 이렇게 댓글 남깁니다. 혹시 제가 db 에서 저 채팅의 내용을 수정하였는데 그것이 채팅창에 반영이 되게 하려면 어떤 방법을 써야 하나요?

          2. 데이터를 Ajax로 수신할 때와 화면에 표시할 때, Primary Key를 심어둔다면 기존 내용의 수정이 가능합니다. 제가 구현한다면 아래와 같은 방법을 따를 것 같습니다.

            1. 먼저 채팅내용을 담은 DB Table에 “수정일”을 담을 필드를 추가합니다.

            2. proc.php에서 출력할 데이터를 DB에서 SELECT할 때, 그 기준을 작성일이 아닌 수정일로 수정합니다. 또한 SELECT한 데이터를 JSON으로 출력할 때, 해당 채팅 레코드의 Primary Key 값을 담는 no라는 이름의 속성을 하나 둡니다.

            3. this.show()에서 화면에 표시할 때, 먼저 해당 Primary Key로 화면에 이미 표시된 <dd> 요소가 있는지 확인합니다. 이미 요소가 있다면 가져온 채팅내용을 해당 요소 안에 덮어쓰고, 그렇지 않다면 새롭게 <dd> 요소를 생성합니다.

            3번 과정을 구현하는 방법은 여러 가지가 있을 수 있는데, 제 경우에는 Custom data Attribute를 활용하는 것을 권해드립니다. 코드로 보여드리면 아래와 같습니다. 실제 실행을 해보지는 않았기 때문에 잘 실행될지는 보증해드릴 수 없지만, 전반적인 흐름을 확인하시기에는 충분하실 것 같습니다.

            if(var o = document.querySelector(“dd[data-no='” + data[i].no + “‘]”)
            {
              o.innerHTML = data[i].msg;
            }
            else
            {
              dd = document.createElement(‘dd’);
              dd.dataset.no = data[i].no;
              dd.appendChild(document.createTextNode(data[i].msg));
              o.appendChild(dd);
            }

  10. 안녕하세요! 좋은 글 감사합니다!
    한가지 질문이 있어 댓글을 남김니다.

    크롬에서 실행할 때 입력한 채팅내용이 뜨지 않습니다. 또한 DB에 INSERT가 되지 않습니다.
    어떻게 해결해야 하는지 조언을 구하고 싶습니다.

    1. ek6055님, 안녕하세요. 덧글 감사드립니다.

      말씀해주신 내용 만으로 가장 가능성 있는 원인을 짐작해보면, Ajax로 호출한 proc.php에서 PHP Warning 또는 Fatal error가 출력되고 있을 가능성이 높으며, 대게 그 내용은 DB에 관련된 오류가 아닐까 추정됩니다. 이 때는 Ajax가 끝난 후에 응답 메시지도 JSON으로 파싱하지 못하게 되므로, Ajax 통신을 마친 후에 화면에서 입력한 채팅내용도 뜨지 않게 됩니다.

      Chrome 브라우저를 이용하고 계시다면, [F12]키로 열 수 있는 개발자 도구에서 Network 탭을 열어두신 후 채팅내용을 입력해보시어 진단해보실 것을 권유해드립니다. Ajax로 proc.php을 호출하였을 때에 응답된 내용을 열어보시면 문제의 원인을 바로 확인하실 수 있으실 것입니다.

      1. 답변 감사합니다! Network탭에서 proc.php파일의 Response를 확인해 보았는데 proc.php파일의 내용이 나와있습니다. 정상적으로 돌아가고있는 것이 맞나요??

        그리고 Network탭에 있는 console창에 아래와 같은 에러가 뜹니다. proc.php.파일과 관련된 에러인가요?
        Uncaught SyntaxError: Unexpected token < in JSON at position 0
        at JSON.parse ()
        at XMLHttpRequest.xmlHttp.onreadystatechange (chat.js:16)

        (chat.js:16) -> res = JSON.parse(xmlHttp.responseText);

        1. ek6055님, 회신 감사합니다. 말씀하신 것과 같이 PHP 소스코드의 내용이 그대로 나오는 것이 원인이라면, short_open_tag 문제로 보입니다. proc.php과 write.php의 소스코드 첫 줄을 “〈?” 대신 “〈?php”로 변경하신 후에 실행해보시는 것을 권해드립니다. short_open_tag 문제에 관련해서는 아래 블로그 포스팅을 참고하시면 도움이 될 것이라고 생각합니다.

          https://extbrain.tistory.com/21

          1. 빠른 댓글 갑사합니다!! short_open_tag변경 후 실행하였더니 응답이
            SELECT * FROM chat WHERE date > “2020-05-30 11:44:09″{“date”:”2020-05-30 11:44:09″,”data”:[]}
            이런 식으로 잘 나오는 듯 합니다. 감사합니다!

            하지만 console창에 나오는 에러 메시지는 사라지지 않습니다.

            Uncaught SyntaxError: Unexpected token S in JSON at position 0
            at JSON.parse ()
            at XMLHttpRequest.xmlHttp.onreadystatechange (chat.js:20)

            (chat.js:20) -> res = JSON.parse(xmlHttp.responseText);

            이 에러 메시지가 발생하는 원인이 무엇일까요…?

          2. 응답 메시지에 SELECT 쿼리문이 섞여서 출력되어 JSON parsing에 실패한 것으로 보입니다. SELECT 쿼리문이 출력되지 않도록 수정하시면 문제가 해결될 것으로 보입니다.

          3. JSON parsing에 대한 에러를 해결했습니다! 감사합니다.

            계속 질문드려서 죄송합니다…

            채팅 내용이 나오지 않아서 질문 드립니다. 혹시 write.php 응답이 php소스 그대로 나오는데 이게 맞는 건가요??

          4. 말씀해주신 문제는 proc.php와 동일하게 short_open_tag 문제가 원인으로 보입니다. write.php의 소스코드 첫 줄을 “〈?” 대신 “〈?php”로 변경하셨는지 확인해보실 것을 권해드립니다.

  11. 좋은 내용 공유해주셔서 감사합니다.
    제이쿼리 ajax는 많이 사용하였지만 실시간 반영에 대한 것은 생각해본적이 없었네요… ㅎㅎ

  12. 너무 좋은 내용 감사합니다! 혹시 다운로드가 안되서 그러는데 보내주실 수 있으신가요??

Leave a Reply

Your email address will not be published. Required fields are marked *