- 게시물 추가, 수정, 삭제, 좋아요, 조회수 표시
- 댓글 추가, 수정, 삭제, 추천
- 해시태그 추가, 해시태그 별 조회
- 카테고리 별 조회
- 게시물 검색
- 회원가입
- 로그인
- 로그아웃
- 소셜 로그인 (구글, 네이버, 카카오)
- 비밀번호 찾기 (이메일로 임시 비밀번호 발급)
- 비밀번호 변경
- 회원정보 수정
- 다른 회원 정보 조회, 구독
- 사용자 닉네임 검색
- 오늘의 명언
- 현재 대전 날씨, 시간
<h3 class="border-bottom pb-2 mt-3">글쓰기</h3>
<div>
<form method="post" th:object="${postForm}">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<div th:replace="~{form_errors :: formErrorsFragment}"></div>
<div class="my-3" style="width:20%">
<select th:field="*{category}" class="form-select">
<option value="" selected>카테고리를 선택하세요</option>
<option value="일기장">일기장</option>
<option value="개발 일지">개발 일지</option>
<option value="잡담">잡담</option>
</select>
</div>
<div class="mb-3">
<label for="subject" class="form-label fs-5">제목</label>
<input type="text" class="form-control" th:field="*{subject}" style="font-size:13pt;">
</div>
<div class="mb-3">
<label for="content" class="form-label fs-5">내용</label>
<div id="editor"></div>
</div>
<div class="mb-3">
<label for="tag" class="form-label fs-5">태그</label>
<div class="input-group">
<input type="text" class="form-control" style="font-size:13pt;" id="inputTag" placeholder="#태그이름" name="inputTag">
<div class="btn btn-outline-light" style="background:lightcoral; border:lightcoral;">등록
</div>
</div>
</div>
<div class="d-flex justify-content-end">
<input onclick="saveEditorContent()" type="submit" value="저장" class="btn btn-outline-light" style="background:lightcoral">
</div>
<input type="hidden" th:field="*{content}" id="content">
</form>
</div>
@PreAuthorize("isAuthenticated()")
@GetMapping("/create")
public String create(PostForm postForm, Model model, Principal principal, @RequestParam(value = "nickname", defaultValue = "") String nickname) {
SiteUser user = this.userService.getUser(principal.getName());
List<SiteUser> authorList = this.userService.searchUser(nickname);
model.addAttribute("user", user);
model.addAttribute("authorList", authorList);
model.addAttribute("searchKw", nickname);
return "post_form";
}
@PreAuthorize("isAuthenticated()")
@PostMapping("/create")
public String create(@Valid PostForm postForm, BindingResult bindingResult, Principal principal, Model model, @RequestParam(value = "inputTag", defaultValue = "") String hashtag) {
SiteUser user = this.userService.getUser(principal.getName());
model.addAttribute("user", user);
if (bindingResult.hasErrors()) {
return "post_form";
}
this.postService.createPost(postForm.getSubject(), postForm.getContent(), postForm.getCategory(), user, hashtag);
return "redirect:/";
}
const quotes = [
"자신의 능력을 믿어야 한다. 그리고 끝까지 굳세게 밀고 나가라. -로잘린 카터",
"할 수 있는 일을 해낸다면, 우리 자신이 가장 놀라게 될 것이다. -토마스 A. 에디슨",
"불가능해 보이는 것은 불확실한 가능성보다 항상 더 낫다. -아리스토텔레스",
"나는 삶에서 언제나 치열함을 추구하라고, 삶을 만끽하라고 배웠다. -헬렌 켈러",
"성공한 사람이 아니라 가치있는 사람이 되기 위해 힘쓰라 -알버트 아인슈타인",
"시간은 환상이다. 점심시간은 두 배로 그렇다. -더글러스 애덤스",
"미래는 현재 우리가 무엇을 하는가에 달려 있다. -마하트마 간디",
"낭비한 시간에 대한 후회는 더 큰 시간 낭비이다. -메이슨 쿨리",
"현재뿐 아니라 미래까지 걱정한다면 인생은 살 가치가 없을 것이다. -윌리엄 서머셋 모옴",
"왜 굳이 의미를 찾으려 하는가? 인생은 욕망이지, 의미가 아니다. -찰리 채플린"
];
function updateQuote() {
const quoteContainer = document.getElementById("quote-container");
const randomIndex = Math.floor(Math.random() * quotes.length);
const newQuote = quotes[randomIndex];
quoteContainer.innerHTML = `<p>${newQuote}</p>`;
}
updateQuote();
const apiUrl = "https://api.openweathermap.org/data/2.5/weather?q=Daejeon&lang=kr&appid=c91e22b768bbc74e94460d26396a2d5f"
const temperatureElement = document.getElementById("temperature");
const weatherDescriptionElement = document.getElementById("weather-description");
const weatherIconElement = document.getElementById("weather-icon");
fetch(apiUrl)
.then(response => response.json())
.then(data => {
const weatherDescription = data.weather[0].description;
const temperatureKelvin = data.main.temp;
const temperatureCelsius = (temperatureKelvin - 273.15).toFixed(1);
temperatureElement.textContent = `${temperatureCelsius}°C`;
weatherDescriptionElement.textContent = `${weatherDescription}`;
const iconCode = data.weather[0].icon;
const iconUrl = `https://openweathermap.org/img/wn/${iconCode}.png`;
const iconImage = document.createElement("img");
iconImage.src = iconUrl;
weatherIconElement.appendChild(iconImage);
})
.catch(error => {
console.error("날씨 정보를 가져오는 중 오류가 발생했습니다.", error);
});
const currentDateElement = document.getElementById("current-date");
const currentDate = new Date();
const year = currentDate.getFullYear();
const month = currentDate.getMonth() + 1;
const day = currentDate.getDate();
const formattedDate = `${year}년 ${month}월 ${day}일`;
currentDateElement.textContent = `${formattedDate}`;
const currentTimeElement = document.getElementById("current-time");
function displayCurrentTime() {
const currentTime = new Date();
const hours = currentTime.getHours().toString().padStart(2, '0');
const minutes = currentTime.getMinutes().toString().padStart(2, '0');
const seconds = currentTime.getSeconds().toString().padStart(2, '0');
const formattedTime = `${hours}:${minutes}:${seconds}`;
currentTimeElement.textContent = `${formattedTime}`;
}
displayCurrentTime();
setInterval(displayCurrentTime, 1000);
@PostMapping("/subscribe/{username}")
public String subscribe(@PathVariable("username") String username, Principal principal) {
SiteUser user1 = this.userService.getUser(principal.getName());
SiteUser user2 = this.userService.getUser(username);
this.userService.subscribeUser(user1, user2);
return String.format("redirect:/user/page/%s", username);
}