본문 바로가기
카테고리 없음

24.10.21 대화하기 만들기

by 융기융 2024. 10. 21.
반응형

@Controller

@RequiredArgsConstructor

@RequestMapping("chatting")

public class ChattingController {

 

private final ChattingService service;

 

 

// /chatting

/** 채팅페이지 전환

* @return

*/

@GetMapping("")

public String chattingPage(

@SessionAttribute("loginMember") Member loginMember,

Model model) {

 

List<ChattingRoom> roomList

= service.selectRoomList(loginMember.getMemberNo());

 

model.addAttribute("roomList", roomList);

 

return "chatting/chatting";

}

 

/** 채팅 상대 검색

* @param query : 상태 닉네임 또는 이메일

* @param loginMember : 로그인한 회원정보

* @return 검색 결과

*/

@GetMapping("selectTarget")

@ResponseBody

public List<Member> selectTarget(

@RequestParam("query") String query,

@SessionAttribute("loginMember") Member loginMember

){

return service.selectTarget(query, loginMember.getMemberNo());

 

}

 

/** 채팅방입장(처음 채팅이면 채팅방생성(INSERT))

* @param targetNo

* @param loginMember

* @return 두 회원이 포함된 채팅방 번호

*/

@ResponseBody

@PostMapping("enter")

public int chattingEnter(

@RequestBody int targetNo,

@SessionAttribute("loginMember") Member loginMember) {

 

int chattingNo

= service.chattingEnter(targetNo, loginMember.getMemberNo());

 

 

return chattingNo;

}

 

/**

* 로그인한 회원이 참여한 채팅방 목록 조회

* @param loginMember

* @return

*/

@GetMapping("roomList")

@ResponseBody

public List<ChattingRoom> selectRoomList(

@SessionAttribute("loginMember") Member loginMember){

return service.selectRoomList(loginMember.getMemberNo());

}

 

/** // 특정 채팅방의 메시지 모두 조회하기

* @param chattingNo

* @param loginMember

* @return

*/

@GetMapping("selectMessage")

@ResponseBody

public List<Message> selectMessage(

@RequestParam("chattingNo") int chattingNo,

@SessionAttribute("loginMember") Member loginMember) {

 

return service.selectMessage(chattingNo, loginMember.getMemberNo());

}

 

/** 채팅 읽음 표시

* @param chattingNo

* @param loginMember

* @return

*/

@PutMapping("updateReadFlag")

@ResponseBody

public int putMethodName(

@RequestBody int chattingNo,

@SessionAttribute("loginMember") Member loginMember) {

 

return service.updateReadFlag(chattingNo, loginMember.getMemberNo());

}

 

@Component // 빈 등록

@Slf4j

public class ChattingWebsocketHandler extends TextWebSocketHandler{

 

@Autowired

private ChattingService service;

 

// WebSocketSession :

// - HTTP Session 객체를 가로챈 값을 가지고 있는 객체

// - 클라이언트 - 서버 전이중 통신담당 (중요한 객체!)

 

// synchronizedSet : 동기화된 Set(충돌 방지, 속도 조금 느림)

private Set<WebSocketSession> sessions

= Collections.synchronizedSet(new HashSet<>());

 

// 클라이언트 연결이 완료된 후 수행

@Override

public void afterConnectionEstablished(WebSocketSession session) throws Exception {

 

// 연결된 클라이언트의 session을 Map에 저장

// -> 연결된 클라이언트를 목록화

sessions.add(session);

}

 

// 클라이언트와의 연결이 종료되었을때

@Override

public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {

sessions.remove(session); // 목록에서 제거

}

 

// 클라이언트로부터

// SockJS.send() 구문을 이용해 텍스트 메시지가 전달된 경우

@Override

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

 

// : message : {"targetNo":"2","messageContent":"ㅁㅁㅁ","chattingRoomNo":"1"}

log.debug("message : {}", message.getPayload());

 

// ObjectMapper : JSON <-> DTO 변환하는 객체(Jackson 라이브러리 제공)

ObjectMapper objectMapper = new ObjectMapper();

 

// 전달받은 JSON 메시지를

// Message 클래스 형태로 변환해서 값을 읽어와

// Message 객체에 대입

Message msg

= objectMapper.readValue(message.getPayload(), Message.class);

 

// 채팅을 보낸 회원의 회원번호 얻어오기

// -> 로그인한 회원번호(session)

// -> WebSocketSession에 담겨있음

HttpSession currentSession

= (HttpSession) session.getAttributes().get("session");

 

// 채팅 보낸 회원

Member sendMember =

((Member)currentSession.getAttribute("loginMember") );

 

int senderNo = sendMember.getMemberNo(); // 보낸 회원번호

 

msg.setSenderNo(senderNo); // Message 객체에 세팅

 

// -------------- DB INSERT ---------------

 

// 1) ChattingService 의존성 주입받기(필드)

 

// 2) INSERT 서비스 호출

// (msg 값 : chattingRoomNo, messageContent, senderNo, targetNo)

int result = service.insertMessage(msg);

 

if(result == 0) return;

 

// 채팅이 보내진 시간을 msg에 기록

SimpleDateFormat sdf

= new SimpleDateFormat("yyyy.MM.dd hh:mm");

 

msg.setSendTime(sdf.format(new Date()));

 

 

 

// -------------- DB INSERT ---------------

 

// 연결된 모든 클라이언트를 순차접근

for(WebSocketSession wss : sessions) {

 

// 채팅방에 입장한 사람들(보낸사람, 받는사람) 에게만

// 메시지(msg) 전달

 

HttpSession clientSession

= (HttpSession)wss.getAttributes().get("session");

 

// 웹소켓 접속 회원목록에서 꺼낸 회원번호

int clientNo

=((Member)clientSession.getAttribute("loginMember"))

.getMemberNo();

 

// 메시지를 보낸사람/받는사람 찾기

if(msg.getTargetNo() == clientNo

|| msg.getSenderNo() == clientNo) {

 

// msg 객체를 JSON으로 변환

TextMessage textMessage

= new TextMessage(objectMapper.writeValueAsString(msg));

 

wss.sendMessage(textMessage);

}

}

}

}

 

 

/*

WebSocketHandler 인터페이스 :

웹소켓을 위한 메소드를 지원하는 인터페이스

-> WebSocketHandler 인터페이스를 상속받은 클래스를 이용해

웹소켓 기능을 구현

 

 

 

WebSocketHandler 주요 메소드

 

void handlerMessage(WebSocketSession session, WebSocketMessage message)

- 클라이언트로부터 메세지가 도착하면 실행

 

void afterConnectionEstablished(WebSocketSession session)

- 클라이언트와 연결이 완료되고, 통신할 준비가 되면 실행

 

void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus)

- 클라이언트와 연결이 종료되면 실행

 

void handleTransportError(WebSocketSession session, Throwable exception)

- 메세지 전송중 에러가 발생하면 실행

 

 

----------------------------------------------------------------------------

 

TextWebSocketHandler :

WebSocketHandler 인터페이스를 상속받아 구현한

텍스트 메세지 전용 웹소켓 핸들러 클래스

 

handlerTextMessage(WebSocketSession session, TextMessage message)

- 클라이언트로부터 텍스트 메세지를 받았을때 실행

 

*/

 

@Component // 빈 등록

public class SessionHandShakeInterceptor

implements HandshakeInterceptor{

 

// 핸들러 동작 전 가로채기

@Override

public boolean beforeHandshake(

ServerHttpRequest request,

ServerHttpResponse response,

WebSocketHandler wsHandler,

Map<String, Object> attributes) throws Exception {

 

// ServerHttpRequest : HttpServletRequest의 부모 인터페이스

// ServerHttpResponse : HttpServletResponse의 부모 인터페이스

 

// attributes : 해당 맵에 세팅된 속성(데이터)은

// 다음에 동작할 Handler 객체에게 전달됨

// (HandshackeInterceptor -> Handler 데이터 전달하는 역할)

 

// ServletServerHttpRequest 상속관계가 맞을경우

if(request instanceof ServletServerHttpRequest) {

 

// 다운캐스팅

ServletServerHttpRequest servletRequest

= (ServletServerHttpRequest)request;

 

// HTTP Session 얻어오기

HttpSession session

= servletRequest.getServletRequest().getSession();

 

// HTTP session을 가로채서 핸들러에 전달

attributes.put("session", session);

}

 

 

return true; // 가로챈 데이터를 핸들러로 전달할지 결정

}

 

@Override

public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,

Exception exception) {

 

}

<select id="selectTarget" resultType="Member">

SELECT

MEMBER_NO, MEMBER_EMAIL, MEMBER_NICKNAME, PROFILE_IMG

FROM

"MEMBER"

WHERE

(MEMBER_EMAIL LIKE '%' || #{query} || '%'

OR

MEMBER_NICKNAME LIKE '%' || #{query} || '%')

 

AND MEMBER_DEL_FL = 'N'

AND MEMBER_NO != #{memberNo}

</select>

 

 

<!-- 두 회원이 참여한 채팅방이 존재하는지 확인 -->

<select id="checkChattingRoom" resultType="_int">

SELECT NVL(SUM(CHATTING_ROOM_NO),0) CHATTING_NO

FROM CHATTING_ROOM

WHERE (OPEN_MEMBER = #{memberNo} AND PARTICIPANT = #{targetNo})

OR (OPEN_MEMBER = #{targetNo} AND PARTICIPANT = #{memberNo})

</select>

 

<!-- 채팅방 테이블 삽입 -->

<insert id="createChattingRoom"

parameterType="map"

useGeneratedKeys="true">

 

<selectKey order="BEFORE" resultType="_int"

keyProperty="chattingNo">

SELECT SEQ_ROOM_NO.NEXTVAL FROM DUAL

</selectKey>

 

INSERT INTO "CHATTING_ROOM"

VALUES(

#{chattingNo},

DEFAULT,

#{memberNo},

#{targetNo}

)

</insert>

 

 

<!-- 채팅방 목록 조회 -->

<select id="selectRoomList" resultType="ChattingRoom">

SELECT CHATTING_ROOM_NO

,(SELECT MESSAGE_CONTENT FROM (

SELECT * FROM MESSAGE M2

WHERE M2.CHATTING_ROOM_NO = R.CHATTING_ROOM_NO

ORDER BY MESSAGE_NO DESC)

WHERE ROWNUM = 1) LAST_MESSAGE

,TO_CHAR(NVL((SELECT MAX(SEND_TIME) SEND_TIME

FROM MESSAGE M

WHERE R.CHATTING_ROOM_NO = M.CHATTING_ROOM_NO), CREATE_DATE),

'YYYY.MM.DD') SEND_TIME

,NVL2((SELECT OPEN_MEMBER FROM CHATTING_ROOM R2

WHERE R2.CHATTING_ROOM_NO = R.CHATTING_ROOM_NO

AND R2.OPEN_MEMBER = #{memberNo}),

R.PARTICIPANT,

R.OPEN_MEMBER

) TARGET_NO

,NVL2((SELECT OPEN_MEMBER FROM CHATTING_ROOM R2

WHERE R2.CHATTING_ROOM_NO = R.CHATTING_ROOM_NO

AND R2.OPEN_MEMBER = #{memberNo}),

(SELECT MEMBER_NICKNAME FROM MEMBER WHERE MEMBER_NO = R.PARTICIPANT),

(SELECT MEMBER_NICKNAME FROM MEMBER WHERE MEMBER_NO = R.OPEN_MEMBER)

) TARGET_NICKNAME

,NVL2((SELECT OPEN_MEMBER FROM CHATTING_ROOM R2

WHERE R2.CHATTING_ROOM_NO = R.CHATTING_ROOM_NO

AND R2.OPEN_MEMBER = #{memberNo}),

(SELECT PROFILE_IMG FROM MEMBER WHERE MEMBER_NO = R.PARTICIPANT),

(SELECT PROFILE_IMG FROM MEMBER WHERE MEMBER_NO = R.OPEN_MEMBER)

) TARGET_PROFILE

,(SELECT COUNT(*) FROM MESSAGE M WHERE M.CHATTING_ROOM_NO = R.CHATTING_ROOM_NO AND READ_FL = 'N' AND SENDER_NO != #{memberNo}) NOT_READ_COUNT

,(SELECT MAX(MESSAGE_NO) SEND_TIME FROM MESSAGE M WHERE R.CHATTING_ROOM_NO = M.CHATTING_ROOM_NO) MAX_MESSAGE_NO

FROM CHATTING_ROOM R

WHERE OPEN_MEMBER = #{memberNo}

OR PARTICIPANT = #{memberNo}

ORDER BY MAX_MESSAGE_NO DESC NULLS LAST

</select>

 

<!-- 특정 채팅방 메시지 조회 -->

<select id="selectMessage" resultType="Message">

SELECT MESSAGE_NO, MESSAGE_CONTENT, READ_FL, SENDER_NO, CHATTING_ROOM_NO,

TO_CHAR(SEND_TIME, 'YYYY.MM.DD HH24:MI') SEND_TIME

FROM MESSAGE

WHERE CHATTING_ROOM_NO = #{chattingNo}

ORDER BY MESSAGE_NO

</select>

 

<!-- 특정 채팅방의 글 중 내가 보내지 않은 글을 읽음 처리 -->

<!-- 채팅 메세지 중 내가 보내지 않은 글을 읽음으로 표시 -->

<update id="updateReadFlag">

UPDATE "MESSAGE" SET

READ_FL = 'Y'

WHERE CHATTING_ROOM_NO = #{chattingNo}

AND SENDER_NO != #{memberNo}

</update>

 

<!-- 메시지 삽입 -->

<insert id="insertMessage">

INSERT INTO "MESSAGE"

VALUES(

SEQ_MESSAGE_NO.NEXTVAL,

#{messageContent},

DEFAULT,

DEFAULT,

#{senderNo},

#{chattingRoomNo}

)

</insert>

/*  Websocket
 -  클라이언트 - 서버 간 전이중 통신
 ex) 전화
  - 양방향
  - 실시간
  - 클1 <-> 서버 <-> 클2

 - ws:// 프로토콜 요청 (http가 아님!!)
  -> HTTP에서 사용하는 request, session을 사용할 수 없음

  --> SessionHandShakeInterceptor가
    HTTP의 Session을 가로채서
    Websocket에서 사용 가능한 형태로 변환
 
  - 어떤 요청가 Websocket 요청인지 지정
*/

// 채팅에 사용될 SockJS 객체를 저장할 변수
let chattingSock;

// 로그인이 되어있을 경우
if(notificationLoginCheck){ // common.html에 선언된 전역 변수

  // 서버로 ws://chattingSock 요청
  // -> 요청으로 처리하는 WebSockHandler와 연결
  //  --> WebSockHandler에 연결된 회원의 정보를 모아두게 된다!!!
  chattingSock = new SockJS("/chattingSock");
}


/* 채팅 메시지를 보내는 함수 */
const sendMessage = () => {

  // 채팅 입력 textarea
  const inputChatting = document.querySelector("#inputChatting");
  const msg = inputChatting.value.trim(); // 입력된 채팅 메시지

  // 로그인이 되어있지 않으면 함수 종료
  if(!notificationLoginCheck) return;

  if(msg.length === 0){ // 채팅 미입력
    alert("채팅을 입력해 주세요");
    return;
  }

  // 웹소켓 핸들러로 전달할 채팅 관련 데이터를 담은 객체 생성
  const chattingObj = {
    "targetNo" : selectTargetNo,    // 메시지를 받을 대상의 회원 번호(웹소켓)
    "messageContent" : msg,         // 전달할 메시지 내용
    "chattingRoomNo" : selectChattingNo // 채팅방 번호(DB 저장용도)
  }

  // JSON으로 변환하여 웹소켓 핸들러로 전달
  chattingSock.send( JSON.stringify(chattingObj) );

  inputChatting.value = ""; // 보낸 채팅 내용 삭제
}

// -----------------------------------------------------------------------------------------



/* 연결된 웹소켓 객체를 통해 서버로 부터 메시지를 전달 받은 경우 */
if(chattingSock != undefined){

  chattingSock.addEventListener("message", e => {
    console.log(e.data);

    // 메소드를 통해 전달받은 JSON을 JS Object로 변환해서 msg 변수에 저장.
    const msg = JSON.parse(e.data);
    console.log(msg);


    // 현재 채팅방을 보고있는 경우
    if(selectChattingNo == msg.chattingRoomNo){


      const ul = document.querySelector(".display-chatting");
   
      // 메세지 만들어서 출력하기
      //<li>,  <li class="my-chat">
      const li = document.createElement("li");
   
      // 보낸 시간
      const span = document.createElement("span");
      span.classList.add("chatDate");
      span.innerText = msg.sendTime;
   
      // 메세지 내용
      const p = document.createElement("p");
      p.classList.add("chat");
      p.innerHTML = msg.messageContent; // br태그 해석을 위해 innerHTML
   
      // 내가 작성한 메세지인 경우
      if(loginMemberNo == msg.senderNo){
        li.classList.add("my-chat");
       
        li.append(span, p);
       
      }else{ // 상대가 작성한 메세지인 경우
        li.classList.add("target-chat");
   
        // 상대 프로필
        const img = document.createElement("img");
        img.setAttribute("src", selectTargetProfile);
       
        const div = document.createElement("div");
   
        // 상대 이름
        const b = document.createElement("b");
        b.innerText = selectTargetName; // 전역변수
   
        const br = document.createElement("br");
   
        div.append(b, br, p, span);
        li.append(img,div);
   
      }
   
      ul.append(li)
      ul.scrollTop = ul.scrollHeight; // 스크롤 제일 밑으로
    }

    selectRoomList();

  })
}









// ---------------------------------------------------------
let selectChattingNo; // 선택한 채팅방 번호
let selectTargetNo; // 현재 채팅 대상
let selectTargetName; // 대상의 이름
let selectTargetProfile; // 대상의 프로필


/* 사용자 검색 팝업 레이어 열기 */
const addTarget = document.querySelector("#addTarget"); // 추가 버튼

const addTargetPopupLayer
  = document.querySelector("#addTargetPopupLayer"); // 팝업 레이어

const closeBtn = document.querySelector("#closeBtn"); // 팝업 닫기

const targetInput = document.querySelector("#targetInput"); // 검색창

const resultArea = document.querySelector("#resultArea"); // 검색 결과 목록


// 추가 버튼 클릭 시
addTarget.addEventListener("click", () => {
  addTargetPopupLayer.classList.remove("popup-layer-close");
  targetInput.focus();
})

// 닫기(X) 버튼 클릭 시
closeBtn.addEventListener("click", () => {
  addTargetPopupLayer.classList.add("popup-layer-close");
  resultArea.innerHTML = ""; // 검색 목록 내용 지우기
})

// 사용자 검색(AJAX)
targetInput.addEventListener("input", () => {

  // 입력된 값
  const query = targetInput.value.trim();

  // 입력된 값이 없을 경우
  if (query.length === 0) {
    resultArea.innerHTML = ""; // 검색 결과 목록 삭제
    return;
  }

  // 입력된 값이 있을 경우
  fetch("/chatting/selectTarget?query=" + query)
    .then(response => {
      if (response.ok) return response.json();
      throw new Error("검색 실패");
    })
    .then(list => {

      console.log(list);

      resultArea.innerHTML = ""; // 이전 검색 결과 비우기

      if (list.length == 0) {
        const li = document.createElement("li");
        li.classList.add("result-row");
        li.innerText = "일치하는 회원이 없습니다";
        resultArea.append(li);
        return;
      }

      for (let member of list) {
        // li요소 생성(한 행을 감싸는 요소)
        const li = document.createElement("li");
        li.classList.add("result-row");
        li.setAttribute("data-id", member.memberNo);

        // 프로필 이미지 요소
        const img = document.createElement("img");
        img.classList.add("result-row-img");

        // 프로필 이미지 여부에 따른 src 속성 선택
        if (member.profileImage == null) img.setAttribute("src", userDefaultImage);
        else img.setAttribute("src", member.profileImage);

        let nickname = member.memberNickname;
        let email = member.memberEmail;

        const span = document.createElement("span");
        span.innerHTML = `${nickname} ${email}`.replace(query, `<mark>${query}</mark>`);

        // 요소 조립(화면에 추가)
        li.append(img, span);
        resultArea.append(li);

        // 클릭 시 채팅방 입장 함수 호출
        li.addEventListener("click", chattingEnter);

      }


    })
    .catch(err => console.error(err));

});


/**
 * 채팅방 입장
 * @param e : 이벤트 객체
 */
const chattingEnter = (e) => {

  // e.currentTarget : 이벤트 리스너가 설정된 요소

  // data-id 값을 얻어와 저장(참여자 회원 번호)
  const targetNo = e.currentTarget.dataset.id;
  // console.log(targetNo);

  fetch("/chatting/enter",{
    method : "POST",
    headers : {"Content-Type" : "application/json"},
    body : targetNo
  })
  .then(response => {
    if(response.ok) return response.text();
    throw new Error("입장 실패");
  })
  .then(chattingNo => {
    // chattingNo : 입장한 채팅방 번호
    console.log(chattingNo);

    selectRoomList(); // 비동기로 채팅방 목록 조회

    // 200ms 후에 실행
    setTimeout(() => {

      // 입장하려던 채팅방이
      // 이미 채팅방 목록에 존재하는 경우

      // 1) 채팅방 목록 li 태그들 얻어오기
      const itemList = document.querySelectorAll(".chatting-item");

      // 2) li 태그의 "chat-no" 값과
      //    입장하려는 방 번호가 같은 경우
      //    == 입장하려는 방이 채팅방 목록에 존재하는 경우
      for(let item of itemList){
        if(item.getAttribute("chat-no") == chattingNo){
          item.focus();
          item.click(); // 클릭 ->  selectChattingFn() 호출됨

          // 검색창 닫기
          addTargetPopupLayer.classList.add("popup-layer-close");

          // 검색창 내용 비우기
          targetInput.value = "";
          resultArea.innerHTML = "";
          return;
        }
      }

    }, 300);

  })
  .catch(err => console.error(err));

}


// -----------------------------------------------
// 비동기로 채팅방 목록 조회
const selectRoomList = () => {

  fetch("/chatting/roomList")
  .then(resp => resp.json())
  .then(roomList => {
    console.log(roomList);

    // 채팅방 목록 출력 영역 선택
    const chattingList = document.querySelector(".chatting-list");

    // 채팅방 목록 지우기
    chattingList.innerHTML = "";

    // 조회한 채팅방 목록을 화면에 추가
    for(let room of roomList){
      const li = document.createElement("li");
      li.classList.add("chatting-item");
      li.setAttribute("chat-no", room.chattingRoomNo);
      li.setAttribute("target-no", room.targetNo);

      if(room.chattingRoomNo == selectChattingNo){
        li.classList.add("select");
      }

      // item-header 부분
      const itemHeader = document.createElement("div");
      itemHeader.classList.add("item-header");

      const listProfile = document.createElement("img");
      listProfile.classList.add("list-profile");

      if(room.targetProfile == undefined)
        listProfile.setAttribute("src", userDefaultImage);
      else                
        listProfile.setAttribute("src", room.targetProfile);

      itemHeader.append(listProfile);

      // item-body 부분
      const itemBody = document.createElement("div");
      itemBody.classList.add("item-body");

      const p = document.createElement("p");

      const targetName = document.createElement("span");
      targetName.classList.add("target-name");
      targetName.innerText = room.targetNickname;
     
      const recentSendTime = document.createElement("span");
      recentSendTime.classList.add("recent-send-time");
      recentSendTime.innerText = room.sendTime;
     
     
      p.append(targetName, recentSendTime);
     
     
      const div = document.createElement("div");
     
      const recentMessage = document.createElement("p");
      recentMessage.classList.add("recent-message");

      if(room.lastMessage != undefined){
        recentMessage.innerHTML = room.lastMessage;
      }
     
      div.append(recentMessage);

      itemBody.append(p,div);

      // 현재 채팅방을 보고있는게 아니고 읽지 않은 개수가 0개 이상인 경우 -> 읽지 않은 메세지 개수 출력
      if(room.notReadCount > 0 && room.chattingRoomNo != selectChattingNo ){
        const notReadCount = document.createElement("p");
        notReadCount.classList.add("not-read-count");
        notReadCount.innerText = room.notReadCount;
        div.append(notReadCount);

      } else if(selectChattingNo !== undefined
            && room.chattingRoomNo == selectChattingNo){

        // 현재 채팅방을 보고있는 경우
        // 비동기로 해당 채팅방 글을 읽음으로 표시
        fetch("/chatting/updateReadFlag",{
          method : "PUT",
          headers : {"Content-Type": "application/json"},
          body : selectChattingNo
        })
        .then(resp => resp.text())
        .then(result => console.log(result))
        .catch(err => console.log(err));

      }
     

      li.append(itemHeader, itemBody);
      chattingList.append(li);
    }

    roomListAddEvent();
  })
  .catch(err => console.log(err));
}

// ------------------------------------

// 채팅방 목록에 이벤트를 추가하는 함수
const roomListAddEvent = () => {
  const chattingItemList = document.getElementsByClassName("chatting-item");
 
  for(let item of chattingItemList){
    item.addEventListener("click", e => {
 
      // 전역변수에 채팅방 번호, 상대 번호, 상태 프로필, 상대 이름 저장
      selectChattingNo = item.getAttribute("chat-no");
      selectTargetNo = item.getAttribute("target-no");

      selectTargetProfile = item.children[0].children[0].getAttribute("src");
      selectTargetName = item.children[1].children[0].children[0].innerText;

      if(item.children[1].children[1].children[1] != undefined){
        item.children[1].children[1].children[1].remove();
      }
 
      // 모든 채팅방에서 select 클래스를 제거
      for(let it of chattingItemList) it.classList.remove("select")
 
      // 현재 클릭한 채팅방에 select 클래스 추가
      item.classList.add("select");
 
      // 비동기로 메세지 목록을 조회하는 함수 호출
      selectChattingFn();
    });
  }
}


// --------------------------------------------------------------
// 비동기로 메세지 목록을 조회하는 함수
const selectChattingFn = () => {

  fetch(`/chatting/selectMessage?chattingNo=${selectChattingNo}`)
  .then(resp => resp.json())
  .then(messageList => {
    console.log(messageList);

    // <ul class="display-chatting">
    const ul = document.querySelector(".display-chatting");

    ul.innerHTML = ""; // 이전 내용 지우기

    // 메세지 만들어서 출력하기
    for(let msg of messageList){
      //<li>,  <li class="my-chat">
      const li = document.createElement("li");

      // 보낸 시간
      const span = document.createElement("span");
      span.classList.add("chatDate");
      span.innerText = msg.sendTime;

      // 메세지 내용
      const p = document.createElement("p");
      p.classList.add("chat");
      p.innerHTML = msg.messageContent; // br태그 해석을 위해 innerHTML

      // 내가 작성한 메세지인 경우
      if(loginMemberNo == msg.senderNo){
        li.classList.add("my-chat");
       
        li.append(span, p);
       
      }else{ // 상대가 작성한 메세지인 경우
        li.classList.add("target-chat");

        // 상대 프로필
        const img = document.createElement("img");
        img.setAttribute("src", selectTargetProfile);
       
        const div = document.createElement("div");

        // 상대 이름
        const b = document.createElement("b");
        b.innerText = selectTargetName; // 전역변수

        const br = document.createElement("br");

        div.append(b, br, p, span);
        li.append(img,div);

      }

      ul.append(li);
      ul.scrollTop = ul.scrollHeight; // 스크롤 제일 밑으로
    }

  })
  .catch(err => console.log(err));
}




// ---------------------------------------------------------------

// 문서 로딩이 완료된 후
document.addEventListener("DOMContentLoaded", () => {

  // 채팅방 목록에 클릭 이벤트 추가하는 함수 호출
  roomListAddEvent();

  // 보내기 버튼 클릭 시 메시지 보내기
  document.querySelector("#send").addEventListener("click", sendMessage);

  // 채팅 입력 후 엔터 입력 시 메시지 보내기
  document.querySelector("#inputChatting").addEventListener("keyup", e => {
    // 입력한 키가 Enter인 경우
    if(e.key == "Enter"){
      if(!e.shiftKey){ /// shift가 눌러지지 않은 경우
                      // == shift + enter 입력 시 제출 X
        sendMessage();
      }
    }
  })

})
반응형