Portfolio, Project/Project(Programming)

Project - 돈 갚아라 ( 문제 해결 / TMP)

잇(IT) 2023. 11. 24. 20:35

**

- post 전송 시 crsf 까먹지 말기!!

 

** 1024 포트 이하 sudo 꼭 붙여서 실행하기


 

1. git bash에서 프로젝트 폴더로 가서 

./gradlew build

를 통해 jar 파일을 생성한다.

 

- plain은 사용하지 않는다.

- java 설치해야한다.

- 보안 그룹 확인 잘하기

- 앱 계속 유지되게 하기위해 사용한다.

- 애플리케이션 끄는 방법

- 뭔 aws에 배포할 때 return 앞에 /money/moneyForm처럼 /를 맨앞에 붙이게되면 //로 인식되어 오류가 발생한다!!! 조심!!!

 

- filed를 통해 값을 전달 할 수 있다. object 이름이랑 전달 받는 객체명이랑 같으면 매핑되서 전달된다.

- 주소값이 다르기 때문에 문자열이 서로 다른 것으로 인식되기 때문에 equals를 사용해야 한다.

 

- mysql 8.0 이상부터는 이거 씀

 

- 얘 쓰니까 자격 증명 안했다?\

 

 

-------- DB work bench로 접속하는 방법-----------------

 

 aws에서 방화벽을 열어주고 

- 뭐 이거 설정해주고,

 

사용자 만들고 권한 부여를 하고 work bench에서 접속하면 된다.

 

- db 생성할때 위와 같이 해야 초기 charset이 utf-8로 설정된다.

 

 

- 위와 같이 실행 할 때는 sudo를 붙여서 실행해야 한다. -> 1024 이하 포트를 사용할때는 관리자 권한이 필요하다.

 

 


- ajax를 이용하여 로그인 검증하기

<head>
    <meta name="_csrf" th:content="${_csrf.token}"/>
    <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
</head>

<th:block layout:fragment="script">
    <script th:inline="javascript">

        function newContent(){
            var token = $("meta[name='_csrf']").attr("content");
            var header = $("meta[name='_csrf_header']").attr("content");

            var url = "/upBoard/newnew";

            $.ajax({
                url      : url,
                type     : "GET",
                beforeSend : function(xhr){
                    /* 데이터를 전송하기 전에 헤더에 csrf값을 설정 */
                    xhr.setRequestHeader(header, token);
                },
                cache   : false,
                success  : function(result, status){
                    location.href='/upBoard/new';
                },
                error : function(jqXHR, status, error){
                    if(jqXHR.status == 401){
                        alert('로그인 후 이용해주세요');
                        location.href='/members/login';
                    } else{
                        alert(jqXHR.responseText);
                    }
                }
            });
        }
    </script>
</th:block>
@GetMapping("/newnew")
    public ResponseEntity asdf(Principal principal) {
        if (principal != null) {
            return new ResponseEntity(HttpStatus.OK);
        } else {
            return new ResponseEntity(HttpStatus.UNAUTHORIZED);
        }
    }

---

- 여러개의 역할 지정 가능


2023.11.01.WED

 

- 메뉴창 전부 보이게 하고 로그인, 등업 이동 시키기

<div th:fragment="header">

    <head>
        <meta name="_csrf" th:content="${_csrf.token}"/>
        <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
    </head>

    <script th:inline="javascript">

        function billNew() {
            var token = $("meta[name='_csrf']").attr("content");
            var header = $("meta[name='_csrf_header']").attr("content");
            var url = "/bill/billNew";
            $.ajax({
                url: url,
                type: "GET",
                beforeSend: function(xhr) {
                    /* 데이터를 전송하기 전에 헤더에 csrf값을 설정 */
                    xhr.setRequestHeader(header, token);
                },
                cache: false,
                success: function(result, status) {
                    location.href = '/bill/new';
                },
                error: function(jqXHR, status, error) {
                    if (jqXHR.status == 401) {
                        alert('로그인 후 이용해주세요');
                        location.href = '/members/login';
                    } else if (jqXHR.status == 418) {
                        alert('등업 후 이용해주세요');
                        location.href = '/upBoard';
                    } else {
                        alert(jqXHR.responseText);
                    }
                }
            });
        }

        function moneyNew() {
            var token = $("meta[name='_csrf']").attr("content");
            var header = $("meta[name='_csrf_header']").attr("content");
            var url = "/money/moneyNew";
            $.ajax({
                url: url,
                type: "GET",
                beforeSend: function(xhr) {
                    /* 데이터를 전송하기 전에 헤더에 csrf값을 설정 */
                    xhr.setRequestHeader(header, token);
                },
                cache: false,
                success: function(result, status) {
                    location.href = '/money/new';
                },
                error: function(jqXHR, status, error) {
                    if (jqXHR.status == 401) {
                        alert('로그인 후 이용해주세요');
                        location.href = '/members/login';
                    } else if (jqXHR.status == 418) {
                        alert('등업 후 이용해주세요');
                        location.href = '/upBoard';
                    } else {
                        alert(jqXHR.responseText);
                    }
                }
            });
        }
    </script>

    <nav class="navbar navbar-expand-sm bg-primary navbar-dark">
        <button class="navbar-toggler" type="button" data-toggle="collapse"
                data-target="#navbarTogglerDemo03" aria-controls="navbarTogglerDemo03"
                aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <a class="navbar-brand white-text" href="/">홈</a>

        <div class="collapse navbar-collapse" id="navbarTogglerDemo03">
            <ul class="navbar-nav mr-auto mt-2 mt-lg-0">
<!--                <li class="nav-item" sec:authorize="hasAnyAuthority('ROLE_USER')">-->
<!--                    <a class="nav-link" href="/bill/new">채무자 등록</a>-->
<!--                </li>-->
                <li class="nav-item" sec:authorize="true">
                    <a class="nav-link" onclick="billNew()">채무자 등록</a>
                </li>
<!--                <li class="nav-item" sec:authorize="hasAnyAuthority('ROLE_USER')">-->
<!--                    <a class="nav-link" href="/money/new">금액 입력</a>-->
<!--                </li>-->
                <li class="nav-item" sec:authorize="true">
                    <a class="nav-link" onclick="moneyNew()">금액 입력</a>
                </li>

- header 부분에 script 코드 작성 후 적용 -> 이게 layout에 포함 시키려했는데 block을 통해서 script를 작성하면 무슨 이유에서인지 javascript 함수를 찾지 못하는 현상 발생 -> 아마 th:replace랑 layout:fragment의 차이인 것 같지만 추후에 알아보도록.... 여튼 원하는 대로 해결은 되었다.

 


1.

 

@Column(name = "freeBoard_id")

- 기존에 이거 없다가 새롭게 @Column 처음에 안넣고 나중에서 넣어서 만들면 새롭게 name으로 DB에서 새롭게 생성되서 DB 아작난다 조심하자!!!


- 삭제!!! 중요

!!!!!!!!!!!!!!!

!!!!!!!!!!!!!!!

!!!!!!!!!!!!!!!

!!!!!!!!!!!!!!!

!!!!!!!!!!!!!!!

!!!!!!!!!!!!!!!

!!!!!!!!!!!!!!!

 

   <th:block layout:fragment="script">
        <script th:inline="javascript">

            function deleteContent(freeBoardId){
                var token = $("meta[name='_csrf']").attr("content");
                var header = $("meta[name='_csrf_header']").attr("content");

                var url = "/freeBoard/" + freeBoardId + "/delete";

                $.ajax({
                    url      : url,
                    type     : "DELETE", // HTTP DELETE 메서드 사용
                    beforeSend : function(xhr){
                        /* 데이터를 전송하기 전에 헤더에 csrf값을 설정 */
                        xhr.setRequestHeader(header, token);
                    },
                    cache   : false,
                    success  : function(result, status){
                        location.href='/freeBoard';
                    },
                    error : function(jqXHR, status, error){
                    }
                });
            }
        </script>
    </th:block>



 <div th:if="${#authentication.name == freeBoard.getMember().getEmail()}">
        <button th:onclick="'deleteContent(' + ${freeBoard.getId()} + ')'" >삭제</button>
    </div>

1. 위처럼 javascript로 delete 요청을 보낸다

@DeleteMapping("/{freeBoardId}/delete")
    public ResponseEntity<String> delete(@PathVariable("freeBoardId") Long freeBoardId) {
        freeCommentService.deleteFBComment(freeBoardId);
        try {
            freeBoardService.delete(freeBoardId);
            return ResponseEntity.ok("삭제되었습니다.");
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("삭제에 실패했습니다.");
        }
    }

2. 전달 받으면 삭제하면 되는데 -> 현재

- 댓글이 달려 있으면 외래키 참조 때문에 삭제가 안되는 오류가 발생한다.

@Transactional
    public void deleteFBComment(Long id) {
        freeCommentRepository.deleteAllByFreeBoard_Id(id);
    }

- 위 처럼 댓글을 먼저 삭제하는 코드를 작성하면 되는데,  위에 @Transactional을 붙여줘야 한다.

트랜잭션 관리: JPA에서 엔터티를 삭제하기 전에 적절한 트랜잭션을 활성화해야 합니다. 
Spring Framework를 사용하는 경우 @Transactional 어노테이션을 사용하여 메서드나
클래스에 트랜잭션을 설정할 수 있습니다. 이렇게 하면 메서드가 실행될 때 Spring은 트랜잭션을 
시작하고 메서드 실행이 완료되면 트랜잭션을 커밋하거나 롤백합니다.

@Transactional 붙여주는것이 진짜 중요하다 왜냐면 롤백이나 커밋을 보장해주기 때문이다.

 

 

--------- 추가로 th:onclick 코드 작성하는 방법 ~~!!!! 

  <div th:if="${#authentication.name == freeBoard.getMember().getEmail()}">
        <button th:onclick="'deleteContent(' + ${freeBoard.getId()} + ')'" >삭제</button>
    </div>

 

 

---- th:href 사용 법!!!!

 

 


- 테스트 코드 작성 시 설정 사항

 

 

- Querydsl 설정

// --------------- Querydsl 추가
    implementation 'com.querydsl:querydsl-core'
    implementation 'com.querydsl:querydsl-jpa'

    annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa"
    annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
    annotationProcessor 'jakarta.annotation:jakarta.annotation-api'

- gradle에 추가해준다. 

// --------------- Querydsl 추가
implementation 'com.querydsl:querydsl-core'
implementation 'com.querydsl:querydsl-jpa'

annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jpa"
annotationProcessor 'jakarta.persistence:jakarta.persistence-api'
annotationProcessor 'jakarta.annotation:jakarta.annotation-api'


- JavaScript Form 동작 착오에 대하여!!!!!!

<div class="AllNewContent-button">
                <!-- 버튼을 누르면 JavaScript 함수가 호출됩니다. -->
<!--                <button type="submit" class="custom-button">저장</button>-->
                <button type="button" class="custom-button" onclick="showConfirmation()">저장</button>
            </div>
            <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
        </form>
    </div>

    <script>
        function showConfirmation() {
            const billIdSelect = document.querySelector('#billId');
            const selectedBillOption = billIdSelect.options[billIdSelect.selectedIndex];
            const selectedBillName = selectedBillOption.textContent;
            const borrowItemName = document.querySelector('input[name="borrowItemName"]').value;
            const borrowMoney = document.querySelector('input[name="borrowMoney"]').value;
            const payItemName = document.querySelector('input[name="payItemName"]').value;
            const payMoney = document.querySelector('input[name="payMoney"]').value;

            const message = `채무자: ${selectedBillName}\n빌린 사유: ${borrowItemName}\n빌린 돈: ${borrowMoney}\n갚은 사유: ${payItemName}\n갚은 돈: ${payMoney}`;

            if (confirm(message)) {
                console.log("얘까지 정상적으로 출력된다---------------------------------");
                document.querySelector('form').submit();
            } else {
                window.location.reload();
            }
      
        }
        document.querySelector('input[name="borrowMoney"]').value = 0;
        document.querySelector('input[name="payMoney"]').value = 0;
    </script>
</div>
</html>

 

- 갑자기 이 javascript 코드가 실행이 안되는 걸 확인했다....

            <!--            장부 검색-->
            <div style="margin-right: 20px;">
                <form class="form-inline my-2 my-lg-0" th:action="@{/}" method="get">
                    <input name="searchQuery" class="form-control mr-sm-2" type="search" placeholder="장부 검색" aria-label="Search">
                    <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
                </form>
            </div>

<!--            게시판 검색-->
            <div>
                <form class="form-inline my-2 my-lg-0" th:action="@{/freeBoard}" method="get">
                    <input name="searchQuery" class="form-control mr-sm-2" type="search" placeholder="게시판 제목 검색" aria-label="Search">
                    <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
                </form>
            </div>

- 원인이 fragment header에 form이 2개가 생성되어서 

if (confirm(message)) {
                console.log("얘까지 정상적으로 출력된다---------------------------------");
                document.querySelector('form').submit();
            } else {
                window.location.reload();
            }
        }

- 여기서 querySelector에서 form을 찾을 때 가장 위의 form을 실행시키게 된다.

- 그래서 header의 form이 실행되는 것이였다.

<form role="form" method="post" th:object="${moneyFormDto}" class="AllNewContent-form" id="moneyFrom">
           
           .........
           ........
           
           
const moneyForm = document.querySelector('#moneyForm'); // ID가 "moneyForm"인 form 요소를 선택합니다.

            if (confirm(message)) {
                console.log("얘까지 정상적으로 출력된다---------------------------------");
                moneyForm.submit();
            } else {
                window.location.reload();
            }
        }

- form에 id를 부여하고, 아래 javascript에 해당 form에 실행되도록 설정해주면 해결된다.


?????????????????????????????????????????????????????????

        <form role="form" th:action="@{/money/new}" method="post" th:object="${moneyFormDto}" class="AllNewContent-form" id="moneyFrom">

- 원래는 그냥 action을 별도로 작성안해줘도 요청이 온 경로로 post를 보내는 줄 알았는데 위와 같이 form에 id를 붙이게 되면 action을 사용해야 정상 동작한다 이거 왜 이러는건지 모르겠지만 일단 이렇게 사용하자

 


- Querydsl 정렬 시 주의할 점

Pageable pageable = PageRequest.of(0, 10, Sort.by(Sort.Direction.DESC, "id"));

- pageable 객체에 정렬 파라미터를 추가해도 Querydsl에 적용이 되지 않는다.

@Override
    public Page<FreeBoard> getFreeBoardList(FreeBoardSearchDto freeBoardSearchDto, Pageable pageable) {

        QueryResults<FreeBoard> freeBoardQueryResults = jpaQueryFactory
                .selectFrom(QFreeBoard.freeBoard)
                .where(freeBoardTitleLike(freeBoardSearchDto.getSearchQuery()))
//                .where(freeBoardMemberLike(freeBoardSearchDto.getSearchQuery()))
                .offset(pageable.getOffset())
                .limit(pageable.getPageSize())
                .orderBy(QFreeBoard.freeBoard.id.desc())
                .fetchResults();

        List<FreeBoard> content = freeBoardQueryResults.getResults();
        long total = freeBoardQueryResults.getTotal();
        return new PageImpl<>(content, pageable, total);
    }
}
        .orderBy(QFreeBoard.freeBoard.id.desc())

- Querydsl 메서드에서 정렬 코드를 추가해주어야 정렬이 된다.

 

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

인스턴스 graldew 권한 없을 때

sudo chmod +x gradlew
728x90