**
- 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으로 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에 추가해준다.
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
'Portfolio, Project > Project(Programming)' 카테고리의 다른 글
Vue, Spring - 간단한 블로그 만들기 - 2 (글 작성, 글 수정 (1차 마무리)) (1) | 2023.10.03 |
---|---|
Vue, Spring - 간단한 블로그 만들기 - 2 (views 생성 및 Spring 데이터 요청 및 응답) (1) | 2023.10.02 |
Vue, Spring - 간단한 블로그 만들기 - 1 (Vue 설정) (0) | 2023.09.30 |
React - 페이지 구현(2) 글 작성 및 수정 (1) | 2023.09.28 |
React - 페이지 구현(1) (최상위 컴포넌트 (App.js), Home 페이지 작성) (0) | 2023.09.25 |