76일차

페이지를 전체 로딩하지 않고 리턴을 받는 비동기 ajax요청과응답에 대해 배웟다.

78. 아이디 중복체크

ajax를 활용해서 회원가입할때 간단하게 아이디 중복체크를 할 수 있다.
페이지 전체 로딩없이 확인만 받으면되니 ajax를 활용하면된다.

78.1 view

button의 타입을 button으로 지정하지 않으면 submit버튼이 되어버리니 꼭 지정해주자.

<div class="mb-3">
    <label for="idInput" class="form-label">아이디</label>
    <input type="text" id="idInput" class="form-control" name="id" value="${member.id}" />
    <div id="availableIdMessage" class="d-none">사용가능한 ID입니다.</div>
    <div id="notAvailableIdMessage" class="d-none">사용 불가능한 ID입니다.</div>                        
    <button type="button" id="checkIdBtn">아이디 중복 체크</button>
</div>

78.2 ajax js코드

간단하게 아이디를 입력받고 가능하닞 아닌지를 아렬줄 것이다.
컨트롤러에서 {"available" : true or false}의 값으로 값을 전달할 것이다.

// id중복확인버튼이 클릭되면
$("#checkIdBtn").click(function() {
    //입력한 id와 ajax요청 보내서
    const userId = $("idInput").val();
    $.ajax("/member/check/" + userId, {
        success: function(data) {
            if (data.available) {
                //사용가능하다는 메세지 출력
                $("#availableIdMessage").removeClass("d-none");
                $("#notAvailableIdMessage").addClass("d-none");
                checkId = true;
            } else {
                // 사용가능하지 않다는 메시지 출력
                $("#availableIdMessage").addClass("d-none");
                $("#notAvailableIdMessage").removeClass("d-none");
                checkId = false;
            }
        },
        complete: enableSubmit
    });
})

78.3 컨트롤러

응답해야하는것이 JSON형태이니 @ResponseBody를 달아준다.

@GetMapping("/check/{id}")
@ResponseBody
public Map<String, Object> checkId(@PathVariable("id") String id) {

    return service.checkId(id);
}

78.4 서비스

받은 id를가지고 member객체가 있는지 확인한다.

@Override
public Map<String, Object> checkId(String id) {
    Member member = mapper.selectById(id);
    return Map.of("available", member == null);
}

79. 별명중복체크

응용해서 별명과 이메일 중복체크를 해보자.

79.1 ajax js

//별명 중복체크
$("#checkNickNameBtn").click(function() {
    const userNickName = $("#nickNameInput").val();
    $.ajax("/member/check/nickName/" + userNickName, {
        success: function(data) {
            if (data.available) {
                $("#availableNickNameMessage").removeClass("d-none");
                $("#notAvailableNickNameMessage").addClass("d-none");
            } else {
                $("#availableNickNameMessage").addClass("d-none");
                $("#notAvailableNickNameMessage").removeClass("d-none");
            }
        }
    });
})

79.2 컨트롤러

@GetMapping("/check/{nickName}")
@ResponseBody
public Map<String, Object> checkNickName(@PathVariable("nickName") String nickName){
    return service.checkNickName(nickName);
}

79.3 서비스

@Override
public Map<String, Object> checkNickName(String nickName) {
    Member member = mapper.selectByNickName(nickName);
    return Map.of("available", member == null);
}

80. 미흡한것들

중복체크 ajax시 미흡한 것들이 있다.

80.1 조건확인하기

회원가입시 비밀번호와 비밀번호가 같을때만 회원가입 을 활성화시켰다.
중복체크를 모두 하고나서 할수있도록 해주자.

패스워드가 일치하지 않아도 되는 버그가 있다.
클래스로 disabled준것은 클래스랑 상관없이 submit되버린다.
클래스로 disabled을 주지 말고 attribute로 주자.

따로 함수를 만들고 각체크마다 함수가 실행되게 하면된다.

<div class="mb-3">
    <input type="submit" id="signupSubmit" class="btn btn-primary" disabled value="가입" />
</div>

let checkId = false;
let checkEamil = false;
let checkNickName = false;
let checkPassword = false;

function enableSubmit() {
    if (checkId && checkEamil && checkNickName && checkPassword) {
        $("#signupSubmit").removeAttr("disabled");
    } else {
        $("#signupSubmit").attr("disabled", "");
    }
}

// id중복확인버튼이 클릭되면
$("#checkIdBtn").click(function() {
    //입력한 id와 ajax요청 보내서
    const userId = $("inputId").val();
    $.ajax("/member/check/" + userId, {
        success: function(data) {
            if (data.available) {
                //사용가능하다는 메세지 출력
                $("#availableIdMessage").removeClass("d-none");
                $("#notAvailableIdMessage").addClass("d-none");
                checkId = true;
            } else {
                // 사용가능하지 않다는 메시지 출력
                $("#availableIdMessage").addClass("d-none");
                $("#notAvailableIdMessage").removeClass("d-none");
                checkId = false;
            }
        },
        complete: enableSubmit
    });
})

//패스워드, 패스워드 체크 인풋에 키업 이벤트 발생시
$("#pwdInput, #pwdInputCheck, #idInput, #nickNameInput").keyup(function() {
    // 패스워드에 입력한 값
    const pw1 = $("#pwdInput").val();
    // 패스워드 확인에 입력한 값
    const pw2 = $("#pwdInputCheck").val();

    // 같으면
    if (pw1 === pw2 && pw1 != '') {
        // submit버튼활성화 
        // 패스워드가 같다는 메시지 출력
        $("#pwdSuccess").removeClass("d-none");
        $("#pwdFail").addClass("d-none");
        checkPassword= true;
    } else {
        // 그렇지 않으면 비활성화
        // 패스워드가 다르다는 메세지 출력
        $("#pwdSuccess").addClass("d-none");
        $("#pwdFail").removeClass("d-none");
        checkPassword= false;
    }
    enableSubmit();
})

80.2 변경시

모든 것을 체크받고 가입버튼이 활성환된 후 변경을 해도 가입 버튼이 비활성화되지 않는다.
키입력시 false로 만들고 함수를 다시 실행시켜 disabled상태로 만들어준다.

//id에 input에 입력시
$("#idInput").keyup(function() {
    // 아이디 중복확인 다시
    checkId = false;
    $("#availableIdMessage").addClass("d-none")
    $("#notAvailableIdMessage").addClass("d-none")

    // submit 버튼 비활성화
    enableSubmit();
});

81. update 내 풀이

기본적으로 위와 같지만 본인의 이메일이거나 별명을 또 사용할 경우
중복으로 봐서 사용불가능하다고 나온다. 이부분을 주의하면서 해야한다.

81.1 update 중복체크

id는 변경이 불가능하기 때문에 같이 받아와서 db의 기록을 가져온 후 입력된값과 비교를 했다.

@GetMapping("/updateCheck/nickName/{nickName}")
@ResponseBody
public Map<String, Object> updateNickNameCheck(@PathVariable("nickName") String nickName, String id){
    return service.checkUpdateNickName(nickName, id);
}

@Override
public Map<String, Object> checkUpdateNickName(String nickName, String id) {
    Member member = mapper.selectByNickName(nickName);
    Member checkMember = mapper.selectById(id);
    if (nickName.equals(checkMember.getNickName())) {
        return Map.of("available", true);
    }
    return Map.of("available", member == null);
}

81.2 submit버튼 비활성화

이부분도 약간 상황이 애매하다. 하나만 변경해도 되야한다.
패스워드는 입력하지 않아도 될때가 있으니 기본값은 true로 입력될때마다 false로 만들고 조건충족하면 true로 만들엇다.

let checkEmail = false;
let checkNickName = false;
let checkPassword = true;

//nickName input에 입력시
$("#nickNameInput").keyup(function() {
    //닉네임 중복확인 다시
    checkNickName = false;
    $("#availableNickNameMessage").addClass("d-none");
    $("#notAvailableNickNameMessage").addClass("d-none");
    //submit버튼 비활성화
    enableSubmit();
})

$("#checkNickNameBtn").click(function() {
    const userNickName = $("#nickNameInput").val();
    const userId = $("#idInput").val();

    $.ajax("/member/updatenickNameCheck/" + userNickName + "?id=" + userId, {
        success: function(data) {
            if (data.available) {
                $("#availableNickNameMessage").removeClass("d-none");
                $("#notAvailableNickNameMessage").addClass("d-none");
                checkNickName = true;
            } else {
                $("#availableNickNameMessage").addClass("d-none");
                $("#notAvailableNickNameMessage").removeClass("d-none");
                checkNickName = false;
            }
        },
        complete: enableSubmit
    });
})

82. update 강사님풀이

로그인 되어 있으니 authentication을 받는 전략을 사용하셨다.
나랑 비슷하게 흘러가는데 아이디를 받는 방법만 다를 뿐이다.

82.1 update 중복체크

//별명 중복체크
$("#checkNickNameBtn").click(function() {
    const userNickName = $("#nickNameInput").val();
    const userId = $("#InputId").val();

    $.ajax("/member/updatenickNameCheck/" + userNickName + "?id=" + userId, {
        success: function(data) {
            if (data.available) {
                $("#availableNickNameMessage").removeClass("d-none");
                $("#notAvailableNickNameMessage").addClass("d-none");
                checkNickName = true;
            } else {
                $("#availableNickNameMessage").addClass("d-none");
                $("#notAvailableNickNameMessage").removeClass("d-none");
                checkNickName = false;
            }
        },
        complete: enableSubmit
    });
})

@GetMapping("/checknickName/{nickName}")
@ResponseBody
public Map<String, Object> checkNickName(@PathVariable("nickName") String nickName, Authentication authentication) {
    return service.checkNickName(nickName, authentication);
}

@Override
public Map<String, Object> checkNickName(String nickName, Authentication authentication) {
    Member member = mapper.selectByNickName(nickName);
    if (authentication != null) {
        Member checkMember = mapper.selectByNickName(authentication.getName());
        return Map.of("available", member == null || nickName.equals(checkMember.getNickName()));
    } else {
        return Map.of("available", member == null);
    }
}

82.2 submit버튼 비활성화

이부분은 비슷하게 갔다.

발전 시키기.

let checkEmail = true;
let checkNickName = true;
let checkPassword = true;

시작시에 전부다 true로 두면 하나만 고쳐도 나머지는 이미 true이기에
하나만 고쳐도 된다.!!

멍청하게 if문으로 비교하고 했었다.

83 좋아요

좋아요는 ajax로 할것이다 누를 때 새로고침 되지 않아야하기 때문이다.

좋아요를 어떻게 테이블에서 표현하나
로그인 사람만 누르나 아니냐는 정하기 나름이다.
한사람이 하나의 게시글엔 하나의 좋아요만 누르게 하나 여러번 눌리나도 정하기 나름이다.

일단 로그인한사람이 한사람당 하나만 좋아요 누를 수 있게 하자.

새 컬럼을 추가하면 1번 게시글에 a,b,c,d,e 원자적이지 않다.
어떤사람이 어떤 게시글에 좋아요눌럿는지 알아야 하기 때문에 새로운 테이블이 필요하다.

테이블 만들때 사용하는 공식이 있다.

표현이 어떤 게시글을 좋아요 누를 것이다.
하나의 게시글을 여러 사람이 누를 수 있다.
하나의 회원이 여러 게시글을 누를 수 있다.

다 대 다 관계이다. n : m관계라고도 부른다.
이럴때 필요한 것이있는데 중간 테이블이 꼭 필요하다.

게시물 1-다 게시물_회원 다 - 1 회원
다 대 다 관계를 꼭 해소해야한다.

이 가운데 테이블이 좋아요 테이블이다.

좋아요 테이블은 게시물 번호, 회원ID적어도 두개의 테이블이 필요하게 된다.

CREATE TABLE BoardLike (
    boardId INT NOT NULL,
    memberId VARCHAR(20) NOT NULL,
    PRIMARY KEY(boardId, memberId),
    FOREIGN KEY(boardId) REFERENCES Board(id),
    FOREIGN KEY(memberId) REFERENCES Member(id)
);

83.1 view

일단 간단하게 좋아요 아이콘 넣기 빈하트 누르면 꽉찬하트

<div>
     <h1>
         <span id="likeIcon">
             <i class="fa-regular fa-heart"></i>
         <!-- <i class="fa-solid fa-heart"></i> -->
         </span>
         <span id="likeNumber">
         ${board.like}
         </span>
     </h1>
</div>

$("#likeIcon").click(function() {
    // 게시물 번호 request body에 추가
    const boardId = $("#boardIdText").text().trim();
    // const data = {boardId : boardId};
    const data = {boardId};

    $.ajax("/like", {
        method: "post",
        contentType: "application/json",
        data: JSON.stringify(data),

        //success:,
        //error:,
        //complete:,
    });
});

83.2 컨트롤러

@PostMapping("/like")
@ResponseBody
public Map<String, Object> like(@RequestBody Like like, Authentication authentication) {
    return service.like(authentication, like);
}

83.3 서비스

@Override
public Map<String, Object> like(Authentication authentication, Like like) {
    like.setMemberId(authentication.getName());
    Integer cnt = likeMapper.insert(like);
    return null;
}

83.4 매퍼

@Mapper
public interface BoardLikeMapper {

    @Insert("""
            INSERT INTO BoardLike
            VALUES(#{boardId}, #{memberId})
            """)
    Integer insert(Like like);


    @Delete("""
            DELETE FROM BoardLike
            WHERE boardId = #{boardId}
            AND memberID = #{memberId}
            """)
    Integer delete(Like like);
}

지웟는데 지워졋으면 좋아요 취소이다.
안지워지면 좋아요 눌리는 것이다.
그래서 지우는 것을 먼저 실행해주자.

@Override
public Map<String, Object> like(Authentication authentication, Like like) {
    Map<String, Object> result = new HashMap<>();

    result.put("like", false);

    like.setMemberId(authentication.getName());
    Integer deleteCnt = likeMapper.delete(like);

    if (deleteCnt != 1) {
        Integer insertCnt = likeMapper.insert(like);
        result.put("like", true);
    } 

    return result;
}

이러면 컨트롤러는 like를 true 혹은 false를 view에게 준다.
view 는 성공한다면 꽉찬하트 아니면 빈하트

$("#likeIcon").click(function() {
    // 게시물 번호 request body에 추가
    const boardId = $("#boardIdText").text().trim();
    // const data = {boardId : boardId};
    const data = {boardId};

    $.ajax("/like", {
        method: "post",
        contentType: "application/json",
        data: JSON.stringify(data),

        success: function(data){
            if(data.like){
                //꽉찬하트
                $("#likeIcon").html(`<i class="fa-solid fa-heart"></i>`);
            } else{
                //빈하트
                $("#likeIcon").html(`<i class="fa-regular fa-heart"></i>`);
            }
        }
        //error:,
        //complete:,
    });
});

2023.05.13 이전글 다음글 문제점

이전글 다음글이 없으면을 고민해보았다.
last글 첫번째글

pageinfo 맵에 담아서 보내자. board는 board로
pageinfo.첫째글 pageinfo.마지막글

남은 기능 좋아요 댓글 기능은 이게 끝
git / github 2~3일배움

내일 팀짜고 조금씩 공부를 할 예정이라고 한다. 다음주부터 프로젝트를 한다고 한다. 매번 그렇지만 매번 급작스럽다.

'국비 > Project - 1 게시판' 카테고리의 다른 글

2023.05.19 80일차 Project  (0) 2023.05.19
2023.05.16 77일차-1 Project  (0) 2023.05.16
2023.05.12 75일차 Project  (0) 2023.05.12
2023.05.11 74일차 Project  (0) 2023.05.11
2023.05.10 73일차 Project  (0) 2023.05.11

+ Recent posts