Could not run build action using Gradle distribution 'https://services.gradle.org/distributions/gradle-5.4.1-bin.zip'.
집에서 작업하던 spring boot 프로젝트를 github를 통해 회사 PC에 check out 했더니 위와 같은 에러가 발생하면서, gradle build가 제대로 되지 않았다. 회사에서 원래 사용하던 sts 3.9.3과 open jdk 9 버전을 사용하고 있던 부분 때문에 생긴 문제로 판단이 되었고, gradle 관련 설정을 수정해야 정상적으로 동작할 것 같았다.
프로젝트의 설정을 들어가보자.
설정을 들어가면 아래와 같이 화면이 나오는데,
기존 사용하던 툴(sts 3.9.3)로 인해 생성된 C:\Users\carrey\.gradle경로를 sts4.2.0이 동일하게 사용하려다 보니,
기존 버전 것 과의 충돌이 일어나거나, 버전 즉 싱크가 어긋나서 제대로 build되지 않았을 것이라고 판단하여, gradle user home 경로를 새로운 경로로 잡아 주었다.
또한 회사 PC에 있는 sts3.9.3은 jdk 9를 사용하였고, 집 PC에 있는 sts4.2.0은 jdk 8을 사용하였다. 이부분이 또 다르게 적용 될 수 있다고 판단이 되어, java home을 jdk8로 집 PC 환경과 동일하게 맞춰 주었다.
@Getter, @Setter : 각 전역변수에 선언해 주면 자동으로 getter, setter 메서드를 생성해 주지 않아도 된다.
public class User {
@Getter
@Setter(AccessLevel.PROTECTED)
private String name;
}
또한 @Setter(AccessLevel.PROTECTED) 접근권한을 제어할 수 있다.(AccessLevel.PUBLIC, AccessLevel.PRIVATE 등등..) AccessLevel.NONE 설정을 통해 메소드 생성을 제한시킬 수 도 있다. -> 잘 쓸 것 같지는 않음
2. NULL 예외처리
@NonNull 말 그대로 NonNull이다. 멤버필드에 선언해 주면, 해당 변수 Setter에 null값이 들어 올 때 NullPointException을 발생시킨다.
public class User {
@NonNull
private String name;
}
3. 현재 객체 정보 출력
@ToString -> 객체.toString() 메서드를 대체하는 어노테이션으로 callSuper값을 true로 할 경우 상속받은 클래스의 정보까지 출력되며, exclude를 통해 제외하고자 하는 변수를 선택할 수 있다. 설정하지 않을 경우 @ToString만 선언하면 된다.
@ToString(callSuper=true,exclude="phoneNumber")
public class User extends Address{
private String name;
private String phoneNumber;
/*
@override
public toString(){
return "Address(super = " + super.toString() +
", name = " + name + ")";
}
*/
}
4. 객체 비교
@EqualsAndHashCode -> 객체 비교에 사용되는 메소드를 대체하는 어노테이션
5. setter+getter+toString+nunNull+equalsAndHashCode 모두 사용할 경우
@Data -> 위의 어노테이션을 합체시킨 어노테이션, 그러나 각 어노테이션에서 설정할 수 있는 세분성까지는 제공하지 못한다. 위에 중요 따로 설정해서 사용할 어노테이션을 설정한 후, 추가적으로 사용하는 것을 권장한다.
@Data(staticConstructor="of") 매개 변수의 값을 원하는 메소드 이름(of)으로 설정하면 생성자를 private로 만들어 주고, 주어진 이름의 static factory method (new를 사용하지 않고 객체를 만들 수 있음)를 만들어준다.
@Data(staticConstructor="of")
public class User {
private String name;
}
public class Addredss {
static public void main(String []args){
User.of().getName();
}
}
6. 자동 자원 close
@cleanup -> try finally에서 close()를 대신 호출해주는 어노테이션
public class User {
static public void main(String [] args){
try {
@Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(new byte[] {'Y','e','s'});
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}
7. 동기화
java의 synchronized를 사용하면 객체 레벨에서 락이 걸려 여러가지 문제가 발생할 수 있다. @Synchronized 어노테이션을 사용하면 가상의 필드 레벨에서 보다 더 안전하게 락을 걸어 준다.
@Synchronized
public class User {
public void ok(){
System.out.println("ok");
}
}
8. 불변 객체 클래스
class에 @Value 어노테이션을 선언하면, 불변 객체를 만들 수 있다.
@Value
public class User {
public String name; //setter 메소드 사용 불가능
}
9. 로그를 편하게
class에 @Slf4j, @Log 등 해당 어노테이션을 선언하면, log관련 static 메소드를 클래스 별로 선언할 필요가 없다.
@Controller
@Slf4j
public class HomeController {
@GetMapping(path="/")
public @responseBody String hello(){
log.info("hello!!!");
return "hello!!";
}
}
10. Builder 사용
class에 @Builder 어노테이션을 선언하면 생성자 대신에 빌더를 사용할 수 있다. 또한 @Singular 어노테이션을 collection 타입에 선언하게 되면 파라미터를 하나씩 추가할 수 있다.
@Builder
public class User {
private Long id;
private String name;
private String password;
@Singular
private List<String> habby;
@Singular
private Map<String, String> maps;
/* Example
User user = User.builder().id(0)
.name("javaboja")
.password("java123")
.habby("football")
.habby("Mountain climbing")
.maps("abc","abc")
.maps("def","def")
.build();
.
*/
}
사실상 업무 시 사용되는 어노테이션은 @Getter, @Setter, @Slf4j 등이 사용되며, 나머지 어노테이션은 가급적이면
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BootSecurityJpa1Application {
public static void main(String[] args) {
SpringApplication.run(BootSecurityJpa1Application.class, args);
}
}
-> 기본으로 생성되는 클래스로 현 페이지에서는 아무런 작업을 하지 않았다.
<MainController.java 소스코드>
package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.demo.service.UserService;
import com.example.demo.vo.User;
@Controller // This means that this class is a Controller
public class MainController {
@Autowired
private UserService userService;
@GetMapping(path="/add") // Map ONLY GET Requests
public @ResponseBody String addNewUser (@RequestParam String name
, @RequestParam String email) {
// @ResponseBody means the returned String is the response, not a view name
// @RequestParam means it is a parameter from the GET or POST request
User n = new User();
n.setName(name);
n.setEmail(email);
userService.userInsert(n);
return "Saved";
}
@GetMapping(path="/all")
public @ResponseBody Iterable<User> getAllUsers() {
// This returns a JSON or XML with the users
return userService.userSelect();
}
}
-> MainController.java의 소스코드 일부는 spring boot 공식사이트에서 발췌한 부분으로 쿼리를 수행하는 userService 객체를 주입받기 위해 @Autowired를 사용했다. 또한 /add는 사용자를 추가하기위함이고, /all은 추가한 사용자를 확인하기 위한 url이다.
<UserRepository.java 소스코드>
package com.example.demo.repository;
import org.springframework.data.repository.CrudRepository;
import com.example.demo.vo.User;
// This will be AUTO IMPLEMENTED by Spring into a Bean called userRepository
// CRUD refers Create, Read, Update, Delete
public interface UserRepository extends CrudRepository<User, Integer> {
}
->하나의 테이블이라고 생각하면 이해가 쉬울 것 같다. CrudRepository 인터페이스를 상속받으며 제네릭 타입중 첫번째는 테이블의 컬럼이 매핑되어있는 클래스(User.java)를 넣고, 두번째는 User테이블의 기본키 타입을 넣는다.
<UserService.java>
package com.example.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.demo.repository.UserRepository;
import com.example.demo.vo.User;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public void userInsert(User user) {
userRepository.save(user);
}
public Iterable<User> userSelect() {
return userRepository.findAll();
}
}
-> UserService.java는 데이터를 컨트롤러에서 받아 받은 데이터로 쿼리를 수행하여 나온 결과를 다시 컨트롤러에 전달해주는 역할을 한다. UserRepository 객체를 주입받으며, sava()는 Insert쿼리를 수행하고, findAll()은 select쿼리를 수행한다.
<User.java 소스코드>
package com.example.demo.vo;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity // This tells Hibernate to make a table out of this class
public class User {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
-> User.java는 하나의 테이블을 의미하며 @Entity 어노테이션을 사용하여 테이블임을 명시하고, 테이블에 해당하는 컬럼들을 변수로 지정하여 Getter, Setter를 구현한다. 또한 @Id 어노테이션을 사용하여 기본키임을 id변수에 명시를 하며, @GeneratedValue(strategy=GenerationType.AUTO) 어노테이션을 사용하여 기본키값을 자동으로 증가하도록 설정하였다.'
자 이제, 제대로 동작하는지 확인해보자.
로컬환경에서 실행하기 위해서는 Run As에서 Spring Boot App를 클릭하자.
(기존 spring 프로젝트 수행시 tomcat에 추가할 필요가 없다.)
<수행 시 화면>
위와 같이 콘솔에 해당 내용들이 출력되었다면, Spring Boot App가 정상적으로 수행된 것이다.
자 이제, 웹 브라우저 또는 HTTP를 호출할 수 있는 tool을 사용하여 사용자를 추가하여 보자.