728x90

테스트를 해보자~!

 

 

처음에는 @SpringBootTest를 이용해 실제 데이터베이스를 조회해서 값을 가져오며 테스트를 해봤다.

그러다 사용하고 있는 Mapper가 잘 동작되는지를 시작으로 Mock을 이용해 테스트를 하게 되었다.

이후 Mock 테스트를 하며 알게 된 것인데,

내가 Mock 객체의 특징을 잘모르고 쓰고 있었구나를 깨달았다. 가짜 객체..그려그려.. 딱 요렇게만 생각했다.

그래서 나름의 깨달음을 토대로, 테스트 하기 전 생각해야할 점들을 적어보았다.

 

먼저 공부했던 개념들을 짧게 정리해보겠다.

테스트하면 단위테스트, 통합테스트를 주로 말하는데 둘의 차이가 뭘까?

 

단위 테스트와 통합테스트의 구분을 나는

 

메소드 단위를 테스트 : 단위테스트

기능 단위를 테스트 : 통합테스트

 

로 얼추 분류하고 있었다. 좀 더 큰 범위가 통합테스트가 아닐까? 하는 막연한 생각을 갖고 있었다.

 

스스로도 분류기준이 애매하다는 찜찜함이 있었다. 

 

찾아본 개념부터 정리하면

- 단위 테스트 Unit Test

: 프로그램의 각 부분을 테스트로 언제든지 수행이 가능해야 한다.

: 외부 종속성과 관련없이 테스트를 진행한다.

- 통합 테스트 Integration Test

: 소프트웨어 모듈간의 통합을 테스트하는 것으로 

: 코드가 외부종속성과 올바르게 작동하는지 확인한다.

 

즉, 분류기준은 외부 종속성과 관련이 있냐, 없냐에 있었다.

 

값이 일치한지 여부를 따지는 메서드를 테스트하고자 할때

@SpringBootTest 를 통한 테스트에서는 실제 데이터베이스의 값을 조회해 비교했다면 

Mock 테스트는 값이 일치한다는 상황을 가정하고 테스트를 한다. 실제 데이터베이스의 값이 필요없는 것이다. (외부 종속성과 관련없다.)

 

결론적으로 SpringBootTest는 단위테스트에 적합하지 않고,통합테스트에 적합하다.

 

개발자는 단위테스트를 통해 자신이 짠 코드를 검증한 후에 통합테스트를 진행한다. 

 

그렇다면 단위테스트에 대해 알아보고 사용해보자

이처럼 종속성없이 테스트를 하기 위해 실제 객체를 사용하지 않고 가짜 객체를 사용하며 테스트하는 것을 

Test Double이라고 한다. Test Double 은 여러 종류가 있다. 이중 많이 사용 되는 것이 Mock객체이다. 

 

(testdouble은 주로 단위 테스트에서 사용되지만, 목적에 따라 다양한 테스트 수준에서 활용될 수 있습니다. 예를 들어, 단위 테스트 외에도 통합 테스트나 시스템 테스트에서도 사용될 수 있습니다. 라고 =chatpgpt가 알려줬다. )

 

Test Double 의 종류를 간단히 살펴보자. (참고)

  • Dummy (사전적 정의: 쓰레기)
    • 사용하지 않는 객체
    • 인스턴스화된 객체가 필요하지만 기능은 필요하지 않은 경우 사용
    • 동작하지 않아도 영향 끼치지 않음
  • Fake (사전적 정의: 가짜)
    • 복잡한 로직이 있는 외부객체동작을 단순화해서 구현한 객체
    • 예로 실제로는 DB를 연결해 테스트해야하지만 이를 단순히 구현한 FakeUserRepository를 만들어 테스트할 수 있다.
  • Stub (사전적 정의: 쓰다 남은 토막)
    • 인터페이스 또는 기본 클래스가 최소한으로 구현된 상태를 제공하는 객체
    • Dummy객체가 실제로 동작하는 것처럼보이게 만들어놓은 객체
    • Mockito프레임워크도 Stub와 같은 역할을 함.
  • Spy (사전적 정의: 염탐하다)
    • Stub객체용으로도 쓰고 호출된 내용을 확인하고자 기록하는 용으로도 쓴다.
    • 실제 객체로도 동작할 수 있다.
    • Mockito 프레임워크의 verify() 메서드가 같은 역할을 한다. 확인용
  • Mock (사전적 정의: 모조품)
    • 호출에 대한 결과값을 미리 적어두고 그 내용에 따라 동작하도록 프로그래밍하는 객체이다.

어떤 상황에서 어떤 것으로 할지 테스트의 목적에 따라 선택해야한다. (코드 참고 )

 

 

<Mock 테스트 하기 전 생각해야할 점들>

1. 무엇을 테스트하고 싶은가?

Mock 객체를 사용한 테스트 뿐 아니라 다른 테스트를 행할 때도 마찬가지로 파악하고 넘어가야하는 지점이다.

어떤 것을 테스트하고 싶은지 정해야한다. 단순히 메서드를 테스트한다고 생각하지 않고

"어떤 것을 실행해서 어떤 결과값을 얻고자 하는지"를 정해야한다. 

2. 어떤 결과값을 얻고 싶은가?

위에도 나와 있듯이 Mock 객체는 " 호출에 대한 결과값을 미리 적어두고 그 내용에 따라 동작하도록 프로그래밍하는 객체"

이기 때문에,

 내 코드가 어떤 결과값을 도출할지를 알고 있어야한다.

3. 결과값이 나오기 위해 필요한 것은?

내가 얻고자 하는 바, 즉 기대하는 결과를 안다면 그 결과가 나오기 위한 객체를 알고 원하는 상황에 맞게 설정해주어야한다.

 

 

이제 세 가지를 생각하고 테스트 코드를 짜보자. 테스트 코드를 짜기 위해 나의 코드를 먼저 살펴보겠다.

 

( 코드 수정하였음! )

 

<로그인 컨트롤러이다.> loginForm 객체로 값을 받으면 유저가 맞는지 확인하기 위해

loginService의 checkLoginForm메서드로 넘긴다.

   @PostMapping("/login")
    public String login(@Valid @ModelAttribute("loginForm") LoginForm loginForm, BindingResult bindingResult, HttpServletResponse response, RedirectAttributes redirectAttributes) {
        if (bindingResult.hasErrors()) {
            log.error("bindingResult.hasErrors-[GetMapping/login]");
            return "/loginForm";

        }

        Optional<User> user = loginService.checkLoginForm(loginForm);

        if (user.isEmpty()) {

            bindingResult.reject("loginFail", "일치하는 회원정보가 없습니다. 다시 시도해주세요");
            return "/loginForm";

        }

        //Cookie
        Cookie idCookie = new Cookie("user_id", String.valueOf(user.get().getUserId()));
        response.addCookie(idCookie);


        redirectAttributes.addAttribute("user_id", user.get().getUserId());

        return "redirect:/closet/{user_id}";


    }

 

<loginService 는 컨트롤러에서 loginForm 값을 넘겨받은 서비스이다.>

checkLoginForm 메서드 안에서 

loginUserService객체의 getUserByEmail메서드로 넘겨받은 loginForm의 이메일 주소를 넘겨

이메일 주소와 같은 유저가 있는지 찾는다. 

그 반환값에 따라 User가 있는 경우, 그 유저의 비밀번호와 loginForm의 비밀번호가 일치한지 확인한다.

일치하면 User를 넘기고 없다면 null을 반환한다.

@Slf4j
@Service
@RequiredArgsConstructor
public class LoginService {

    
    private final LoginUserService loginUserService;

    public Optional<User> checkLoginForm(LoginForm loginForm) {
        String email = loginForm.getEmail();
        User userEmail = User.builder().email(email).build();

        String password = loginForm.getPassword();

        Optional<User> userByEmail = loginUserService.getUserByEmail(userEmail);

        if (userByEmail.isPresent()) {
            return Optional.ofNullable(userByEmail.filter(m -> m.getPassword().equals(password)).orElse(null));

        } else {
            throw new NoSuchElementException("User not found");


        }


    }


}

 

<LoginUserService 는 userMapper를 통해 조회 후 결과값을 반환한다.>

@Service
@RequiredArgsConstructor
public class LoginUserService {
    private final UserMapper userMapper;

    public Optional<User> getUserByEmail(User user) {

        return Optional.ofNullable(userMapper.getByEmail(user.getEmail()));
    }
}

 

 

로그인 로직은 이렇다. 

 

1. 여기서 내가 테스트하고 싶은 것은 

loginService 객체가 유효한 loginForm을 받았을 경우이다. 그 경우 이메일도 일치하고 비밀번호도 일치해야한다. 

 

2. 기대하는 결과는, loginForm의 이메일을 넘겨받은

loginUserService의 getUserByEmail 메서드가 

해당하는 이메일과 비밀번호를 잘 조회해서 User값을 문제없이 응답하는 것이다.

 

3. 그러면 기대하는 결과가 나오기 위해서는 loginUserService객체가 필요하다.

 

일단 완성된 테스트 코드를 보자.

public class LoginServiceTest {


    @Mock
    private LoginUserService loginUserService;
    @InjectMocks
    private LoginService loginService;

    private LoginForm loginForm;
    private User user;

    @BeforeEach
    public void init() {
        MockitoAnnotations.openMocks(this);
        loginForm = LoginForm.builder().email("so@naver.com").password("1234").build();
        user = User.builder().email(loginForm.getEmail()).password(loginForm.getPassword()).build();
    }

    @Test
    @DisplayName("checkLoginForm메서드 : 유저의 이메일이 맞고 이메일의 비밀번호도 맞다")
    void checkLoginFormTest() {
        //given
        //loginUserService가 email에 맞는 user값을 반환했다고 가정하면
        when(loginUserService.getUserByEmail(any())).thenReturn(Optional.of(user));

        //when
        //checkLoginForm 를 호출했을 때
        Optional<User> result = loginService.checkLoginForm(loginForm);

        //then
        //값이 있고 이메일과 비밀번호도 같아야한다.
        assertThat(result).isNotNull();
        assertThat(result.get().getEmail().equals("so@naver.com"));
        assertThat(result.get().getPassword().equals("1234"));


    }




}

 

 

나는 명시적으로 메서드 호출 전에 호출하기 위해  BeforeEach를 사용해 

 MockitoAnnotations.openMocks(this); 로 Mock객체를 만들어주었다. 

 

이거 대신 이 어노테이션을 사용해도 똑같이 필요한 mock객체들을 초기화해서 가져오는 역할을 한다.

(@ExtendWith(MockitoExtension.class)를 사용하는 것이 JUnit 5에 더 적합한 방법이라고 함. )

@ExtendWith(MockitoExtension.class)
class LoginServiceTest {
}

 

기본적으로 Mockito를 사용할 때는 목 객체를 생성하고 초기화 하는 과정이 필요한데

 

@Mock으로 선언할 때 사용한다.  MockitoExtension이 이를 감지해 테스트 메서드 실행 전에 초기화를 수행한다. 

@InjectMocks 을 사용해 필요한 목 객체를 주입받는 객체도 초기화 한다. 

 

내 코드로 보자면, @Mock으로 선언한 LoginUserService 목 객체를 @InjectMocks로 선언한 LoginService에 주입합니다.

그러면 필요한 객체를 가져왔으니 테스트 메서드를 보면, (LoginService가 받는 loginForm을 만들고 타입에 맞게 User로도 변환해주었다. )

 

//given

내가 원하는 결과를 얻기 위해서는 LoginUserService에서 문제없이 User값을 반환해야한다. 그렇게 했다고 가정하고

 

//when

loginService의 checkLoginForm를 실행하면 응답값User가 나온다.

 

//then

그러면 그 User는 입력한 User의 이메일과 비밀번호 값이 동일할 것이다. 

 

 

여기서 내가 많이 헤맨 부분은 given 이었다. 원하는 결과값을 얻기 위해 필요한 상황을 제시해주어야한다.

 

given이 when으로 되어있는 기존 코드에서 통일된 given을 사용하는 BDD(행동 주도 개발, Behavior-Driven Development) 로도 작성해보았다.

       //given
        //loginUserService가 email에 맞는 user값을 반환했다고 가정하면
        given(loginUserService.getUserByEmail(any(User.class))).willReturn(Optional.of(user));

        //when
        //checkLoginForm 를 호출했을 때
        Optional<User> result = loginService.checkLoginForm(loginForm);

        //then
        //값이 있고 이메일과 비밀번호도 같아야한다.
        assertThat(result).isNotNull();
        assertThat(result.get().getEmail().equals("so@naver.com"));
        assertThat(result.get().getPassword().equals("1234"));

        then(loginUserService).should().getUserByEmail(any(User.class));

 

 

그렇다면 지금 checkLoginForm가 잘 동작하는지 확인하기 위해서 

LoginUserService 객체가 올바르게 반환한다! 전제조건을 깔았는데,

 

LoginUserService 객체의 메소드를 테스트하는 코드도 짜봤다. 

1. 테스트하고자 하는것 LoginUserService이 유효한 user값을 받았을 경우이다. 

2. 기대하는 결과는 mapper를 통해 유효하는 user가 검증되었다면 user값을 반환해야한다.

3. 필요한 객체는 userMapper이다.

 

@Slf4j
@ExtendWith(MockitoExtension.class) //초기화로 mock 객체 가져옴
public class LoginUserServiceTest {

    @Mock
    private UserMapper userMapper;

    @InjectMocks
    private LoginUserService loginUserService;
    

    @Test
    void testGetUserByEmail() {
        //Given
        String userEmail = "test@example.com";
        String password = "123";
        User user = User.builder().email(userEmail).password(password).build();
        when(userMapper.getByEmail(userEmail)).thenReturn(user); //조회해서 값이 있다고 가정했을 때

        //when
        Optional<User> result = loginUserService.getUserByEmail(user);

        //Then
        assertThat(result.isPresent()); //값이 있다고 나온다.
        assertEquals(userEmail, result.get().getEmail());
        assertEquals(password, result.get().getPassword());

        //userMapper 목 객체에 대해 getByEmail메서드가 정확히 1번 호출되었는지 검증
        verify(userMapper, times(1)).getByEmail(userEmail);


    }
}

 

 

사실 이렇게만 보면 Mock테스트는

메서드 내에서 필요한 객체들이 문제없이 반환값을 잘 가져왔을 때 

테스트하고자하는 메서드가 잘 동작하는지 확인하는 용도일뿐이다.

 

그래도 테스트 코드를 짜다보면 코드의 문제점을 발견하고 고치게 된다. 

특히 예외테스트가 중요하다고 한다!

 

나도 테스트를 하면서 많은 리팩토링을 거쳤다. 

1. 단일책임 원칙으로 필요한 메서드만 담긴 서비스 객체 만듦.

기존 코드에서는

UserService에서 User값을 조회하고 검증하는 여러개의 메소드를 가진 UserSerivce를 주입했었다.

하지만 이렇게 될 경우 사용하지 않는 메소드들도 모두 주입된다고 하여 

UserLoginService를 따로 만들어서 처리하였다. 

2. 예외테스트를 하면서 예외의 경우 로직 추가 및 optional추가

 

기존 코드에서는

loginUserService.getUserByEmail(userEmail); 는  Optional로 처리하였지만

였고 그 결과값이 존재할때 null이 아닌경우, 해당하는 비밀번호가 있을 경우 return User타입으로 반환을 하였었는데

 

return userByEmail.filter(m -> m.getPassword().equals(password)).orElse(null);

그대로 optional로 반환하였다.

Optional<User> userByEmail = loginUserService.getUserByEmail(userEmail);

if (userByEmail.isPresent()) {
   // 같은 코드임 return Optional.ofNullable(userByEmail.filter(m -> m.getPassword().equals(password)).orElse(null));
    return userByEmail.filter(m -> m.getPassword().equals(password));

} else {
    throw new NoSuchElementException("User not found");


}

 

 

테스트코드는 다음과 같다. 

    @Test
    @DisplayName("조회한 이메일이 없을 때 checkLoginForm 실패")
    void uncheckLoginFormTest() {
    
     	// Given: Mock 객체에 예외를 발생시키도록 설정
        when(loginUserService.getUserByEmail(any())).thenReturn(Optional.empty());
        
        // When: 특정 메서드를 호출 () -> 메서드 호출하면
        // Then: 예외가 발생하는지 확인 NoSuchElementException이 나온다.

        assertThrows(NoSuchElementException.class, () ->
                loginService.checkLoginForm(loginForm)

        );


    }

 

예외테스트가 훨씬 어려운 느낌이다.. 공부 많이 해야지..

 

그리고 처음에 테스트하면서 테스트 코드에 given에

thenReturn에 null값을 넣어놓고

왜 값이 없을 때 else 로 빠져서 NosuchElementException이 나오는게 아니라

userByEmail.isPresent()에 멈춰서 nullpointException이 나지? 라며 계속 헤맸다. 

 

Optional을 검증하는 메서드ispresent를 할 건데 그 값에 null인 값이 설정돼서 그랬다.

 

단위테스트 재밌기도 한데 방향을 놓치면 엄청 헤매거나 의미없는 테스트코드만 만들 수가 있다.

공부 더 해보자 이얏호~!

728x90
728x90

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., 스프링 핵심 원리를 이해하고, 성장하는 백엔드 개발자가 되어보세요! 📢

www.inflearn.com

공부 기록

728x90
728x90

공부 필기 기록

 

읽으면서 너무 재밌었던 책!

저자의 설명도 좋았는데 드립이 너무 웃겼다ㅋㅋㅋ 주변에 마구 추천했다

 

자바가 동작하면서 메모리 상에서 어떤일이 일어나는지를 자세히 살펴볼 수 있어서 좋았다

728x90
728x90

백견불여일타 JSP& Servlet 책 공부를 마치고 

마지막 장에 MVC패턴에 대한 소개가 나왔다. 그 내용을 정리해보겠다~!

(jsp, servlet 공부하느라 적어놓은 것도 블로그에 올려야겠다!)

 

MVC 패턴(= model 2 방식)이란?

초창기 웹 애플리케이션을 작성하는 방식이 model1 이라고 한다. 

model1은 보여주는 화면단 프레젠테이션 로직과 비즈니스 로직단이 엉켜있는 방식이다. 

 

재사용성과 가독성을 위해 비즈니스 로직을 중심으로 하는 model 2방식이 나왔다.

 

model, view , controller 를 사용하는 패턴을 말한다. 

이 3가지 영역으로 나누고 이들의 결합도를 최소화하는 것이 MVC 패턴의 목표이다.

 

모델은 비즈니스 로직, 즉 데이터베이스와 연동하여 서비스를 동작하기 위한 로직을 말하고 

뷰는 결과를 출력하여 보여주는 화면단

컨트롤러는 모델과 뷰를 연결하고 제어하는 역할을 한다. 

이 책에서는 JSP 가 뷰 역할을 해서 화면에 결과물을 출력했고 

서블릿을 이용해 클라이언트의 요청을 받아 로직을 수행했다.

모델은 데이터베이스와 연동하는 비즈니스 로직을 통해 얻어온 데이트를 저장하는 용도로 사용한다. 

 

MVC 패턴의 동작원리는 다음과 같다. 

 

가장 중요한 것은 컨트롤러의 역할!

프로그램에서 컨트롤러는 요청에 따라 어떤 비즈니스 로직을 행해야하는지 결정해주는 역할을 한다. 

또한 모델을 통해 얻은 결과를 어떤 뷰에 보내서 처리할지도 결정한다. 

 

용어를 정리해보자면,

 

DAO란? Data access object 

데이터 접근 객체. 

즉 데이터베이스를 연결하는 역할을 한다. 

연결하고 연결을 끊고. 데이터베이스에 접속해서 처리할 작업들을 하는 객체이다. 

실습 때는 반복적으로 하는 디비연결, 연결 끊기는 별도의 DBManager 객체를 만들어 호출해 사용했다. (싱글톤으로! - 객체를 한번만 사용해서 기존에 생성된 인스턴스를 통해 연결..)

 

DAO 안에서 sql 쿼리를 날렸다.

 

DTO란? Data Transfer object

데이터 전송 객체 

 

나도 헷갈리는 것이 있는데 실습할 때는 VO 객체를 만들고 구현했기 때문에 

DTO와 VO의 구분이 가질 않았다. 책에서도 DTO라고 적고 VO로 사용한 것 같은데..

 

DTO와 VO의 특성을 알고 

어떤 걸 사용할지 고민하면 된다. (알고 보니 실습에서는 VO라고 하고 setter를 사용한 것을 보니 DTO로 쓴 듯하다.)

 

DTO 와 VO의 차이점

둘다 데이터를 다루지만 그 쓰임새가 다르다. 

 

결론적으로 보면

사용목적은 다르지만 사용하는 방식은 비슷해서 혼돈이 온다.

 

VO는 비즈니스 로직을 위한 데이터 상태를 처리하기 위해 주로 쓰여서

로직을 추가할 수 있고 getter 만 사용해 그 자체 값을 가져오고

( VO는 equals, hashcode를 재정의하여 사용해 객체의 주소값 상관없이 

내용이 같다면 동일하다고 본다. )

 

DTO는 데이터 전송에 주로 쓰이고

로직을 추가할 수 없고 setter, getter 를 쓴다는 차이가 있다.  

 

 

아래 코드를 통해 어떻게 사용하는지 살펴보자

 출처 chatgpt

 

 

VO (Value Object)는 주로 데이터의 상태를 나타내고 비즈니스 도메인의 특정 비즈니스 로직을 위해 사용됩니다. 다음은 DTO 대신 VO를 사용하는 경우의 예시와 상황입니다:

1. 도메인 모델 내부에서 사용하는 데이터: VO는 도메인 모델의 일부로 사용되며, 도메인 모델 내부에서 데이터를 표현하고 비즈니스 로직을 수행하는 데 사용됩니다. 예를 들어, 주문, 제품, 고객, 계좌 등과 같은 비즈니스 엔터티의 상태를 나타내기 위해 VO를 사용할 수 있습니다. 

   public class OrderVO {
       private int orderId;
       private List<ProductVO> products;
       private CustomerVO customer;
   
       // 비즈니스 로직을 포함할 수 있음
       public void calculateTotalPrice() {
           // 제품 가격을 합산하고 총 가격을 계산
       }
   }

2. 데이터 검증 및 유효성 검사:VO는 데이터의 유효성을 검사하고 특정 규칙에 따른 검증을 수행하기 위해 사용될 수 있습니다. VO 객체는 데이터의 일관성을 보장하고 비즈니스 로직에 따라 데이터를 처리하는 데 유용합니다.

 

   public class ProductVO {
       private int productId;
       private String name;
       private double price;
   
       public void validate() {
           // 제품 데이터의 유효성을 검사하고 오류를 처리
       }
   }



3. 비즈니스 룰 적용: VO는 비즈니스 룰 및 규칙을 적용하는 데 사용될 수 있습니다. 예를 들어, 할인, 부가세 계산, 주문 처리 등과 같은 비즈니스 규칙을 VO 내에서 구현할 수 있습니다.

 

   public class OrderVO {
       private int orderId;
       private List<ProductVO> products;
       private CustomerVO customer;
   
       public void applyDiscount() {
           // 할인 규칙을 적용하여 주문 가격을 조정
       }
   }



VO는 주로 비즈니스 도메인에서 데이터의 상태와 관련된 작업을 수행하기 위해 사용됩니다. 이러한 작업은 DTO보다 비즈니스 로직과 데이터 처리에 더 중점을 둔 작업이며, VO는 이를 효율적으로 처리할 수 있는 도구로 사용됩니다.

 

 

DTO (Data Transfer Object)는 주로 데이터 전송과 다른 레이어 간의 데이터 교환을 위해 사용됩니다. 다음은 DTO를 사용하는 몇 가지 주요 상황과 예시입니다:

1. 데이터베이스와 비즈니스 레이어 간 데이터 전송:데이터베이스에서 데이터를 검색하고, 이 데이터를 비즈니스 레이어로 가져올 때 DTO를 사용합니다. DTO는 데이터베이스 스키마와 비즈니스 모델 간의 데이터 구조 차이를 처리하고 데이터를 전달하는 역할을 합니다.

   예시:

   public class ProductDTO {
       private int productId;
       private String name;
       private double price;

       // Getter 및 Setter 메서드
   }



2. 서로 다른 시스템 간 데이터 교환: 웹 서비스나 REST API를 통해 다른 시스템으로 데이터를 전송할 때 DTO를 사용합니다. 이렇게 다른 시스템 간의 통신에서는 데이터를 구조화하고 표준 형식으로 전송하기 위한 목적으로 DTO를 활용합니다.

   예시:

   public class OrderRequestDTO {
       private List<ProductDTO> products;
       private CustomerDTO customer;

       // Getter 및 Setter 메서드
   }



3. 뷰 레이어와 비즈니스 레이어 간 데이터 전송: 웹 애플리케이션에서 사용자 인터페이스와 비즈니스 레이어 사이에서 데이터를 전달할 때 DTO를 사용합니다. DTO는 뷰에서 필요한 데이터를 비즈니스 레이어로 전달하고 그 반대로 비즈니스 로직의 결과를 뷰로 반환하기 위해 사용됩니다.

   예시:

   public class OrderFormDTO {
       private String customerName;
       private List<ProductDTO> selectedProducts;

       // Getter 및 Setter 메서드
   }



4. 데이터 필터링: 뷰에서 데이터베이스로 데이터를 요청할 때, DTO를 사용하여 필요한 필드만 선택적으로 전달하는 방식으로 데이터 필터링을 수행할 수 있습니다.

   예시:
 

   public class ProductRequestDTO {
       private boolean includePrice;
       private boolean includeDescription;

       // Getter 및 Setter 메서드
   }



DTO는 주로 데이터의 전송과 데이터 구조 변환을 간소화하고 효과적으로 관리하기 위해 사용됩니다. 데이터를 읽기, 쓰기, 전송하기 위한 간편한 방법을 제공하며, 데이터의 형식이나 구조가 서로 다른 레이어 간에 호환성을 유지하는 데 도움을 줍니다.

 

 

이 둘을 어떻게 사용할지 더 물어봤다. 



여기서는 간단한 예를 통해 설명하겠습니다:

1. DTO를 사용한 접근:

   - `ProductDTO`를 사용하여 데이터베이스에서 제품 정보를 가져옵니다.
   - `OrderVO`에서 이러한 `ProductDTO`를 사용하여 제품 가격을 가져와서 총 가격을 계산합니다.

   public class OrderVO {
       private List<ProductDTO> products;

       public void calculateTotalPrice() {
           double total = 0;
           for (ProductDTO productDTO : products) {
               // ProductDTO에서 제품 가격을 가져와서 합산
               total += productDTO.getPrice();
           }
           // 총 가격을 설정
           // total 값을 다른 VO 속성에 저장
       }
   }



2. DTO를 사용하지 않는 직접적인 접근:

   - `OrderVO`에서 데이터베이스에 직접 연결하여 제품 정보를 가져와 가격을 계산합니다.

 

   public class OrderVO {
       private List<ProductVO> products;

       public void calculateTotalPrice() {
           double total = 0;
           for (ProductVO productVO : products) {
               // 데이터베이스에 연결하여 제품 가격을 가져와서 합산
               // total 값을 다른 VO 속성에 저장
           }
       }
   }


- DTO (Data Transfer Object): 데이터를 전달하고 다른 레이어 간의 데이터 교환을 목적으로 사용됩니다. 주로 데이터를 구조화하고 전달하기 위한 객체입니다.

- DAO (Data Access Object): 데이터베이스와 상호 작용하여 데이터를 검색, 추가, 수정, 삭제하는 역할을 합니다. 데이터베이스 액세스와 데이터 관리를 위한 객체입니다.

- VO (Value Object):주로 비즈니스 도메인의 상태를 나타내며, 불변하고 비즈니스 로직을 수행하기 위한 객체입니다.

일반적으로 DTO는 DAO에서 데이터베이스에서 가져온 데이터를 비즈니스 레이어로 전달할 때 사용됩니다. 이후 비즈니스 로직은 VO나 다른 비즈니스 객체에서 처리됩니다. VO와 DTO는 각각의 역할과 목적을 갖고 있으며, 이러한 분리된 역할은 코드의 가독성, 유지보수성, 테스트 용이성을 개선하는 데 도움을 줍니다.

 

 

 

 

 

 

 

https://velog.io/@gillog/Entity-DTO-VO-%EB%B0%94%EB%A1%9C-%EC%95%8C%EA%B8%B0

728x90
728x90

자바 프로젝트를 데이터베이스에 연결하기 위해서는 다음과 같은 단계를 거친다. 

1. JDBC 드라이버 로드  (JDBC java Database Connectivity 자바와 디비 연결을 돕는 api들을 제공해준다)

2. 데이터베이스와 연결

3. SQL문 실행

4. 데이터베이스와 연결 끊음

 

나는 1번부터 막혔다! (이클립스에서 인텔리제이로 넘어오면서 책에 예시가 없어서 다 찾아봐야했다)

maven이나 gradle을 써서 사용하는 드라이버 설정을 해줘야하나 싶었지만 역시나 해결이 안됨.

 

마리아 디비 드라이버 다운 

https://mariadb.com/downloads/connectors/connectors-data-access/java8-connector

+ java 눌러서 다운받은 드라이버넣어줬는데도

Class.forName("org.mariadb.jdbc.Driver");

부터 통과가 안됐다! 드라이버를 못가져오는거같은데...

 

 

해결방법

 

이 블로그를 보고 도움 받았다!

"WEB-INF/lib 안에 mariadb-java-client-2.6.2.jar을 빌드 해주야 해결이 된다" !

https://jung-story.tistory.com/108

 

Avaliable Elements 안에 TEST 안에 있는 

maraidb 파일을 클릭해서 왼쪽 lib 안에 집어넣는다.

 

드라이버 연결 DBManager

 

package util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

public class DBManager {
    public static Connection getConnection(){


        Connection con = null;

        String server = "서버주소(나는 localhost)";
        String database = "데이터베이스 이름";
        String user_name = "유저네임";
        String password = "비밀번호";

        try {
            Class.forName("org.mariadb.jdbc.Driver");
            System.out.println("드라이브로드 성공");

        } catch (ClassNotFoundException e) {
            System.err.println(" 드라이버 로딩 오류 : " + e.getMessage());
            e.printStackTrace();
        }

        try {
            con = DriverManager.getConnection("jdbc:mariadb://" +
                    server + "/" +
                    database +
                    "?useSSL=false", user_name, password); // SSL 실행 확인
            System.out.println("연결 성공");
        } catch(Exception e) {
            System.err.println("에러 내용 :" + e.getMessage());
            e.printStackTrace();
        }

        System.out.println("con리턴" +con);
        return con;



    }
    //select 수행한 후 리소스 해제를 위한 메소드
    public static void close(Connection conn, Statement stmt, ResultSet rs){
        try{
            rs.close();
            stmt.close();
            conn.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //insert, update, delete 작업을 수행한 후 리소스 해제를 위한 메소드
    public static void close(Connection conn, Statement stmt){
        try{
            stmt.close();
            conn.close();
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

 

ProductDAO

에서 getInstance를 통해 연결!

package com.saeyan.dao;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

import util.DBManager;

import com.saeyan.dto.ProductVO;

public class ProductDAO {
    private ProductDAO(){

    }
    private static ProductDAO instance = new ProductDAO();
    public static ProductDAO getInstance(){
        return instance;
    }

    //c Read u d
    public  List<ProductVO> selectAllProducts(){
        //최근 등록한 상품 먼저 출력하기
        String sql= "select * from product order by code desc";
        List<ProductVO> list = new ArrayList<>();
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try{
            conn = DBManager.getConnection();
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();


            while (rs.next()){
                ProductVO pVo = new ProductVO();
                pVo.setCode(rs.getInt("code"));
                pVo.setName(rs.getString("name"));
                pVo.setPrice(rs.getInt("price"));
                pVo.setPictureUrl(rs.getString("pictureurl"));
                pVo.setDescription(rs.getString("description"));

                list.add(pVo); //앗 얘를 빼먹었었네!
            }


        } catch(Exception e){
            System.out.println("값 못가져옴:"+e.getMessage());


        } finally {
            DBManager.close(conn, pstmt, rs);
        }
        return list;
    }

}
728x90
728x90

인터페이스(구현을 위한 껍데기로 틀을 잡아주는 역할을 한다)와 추상 클래스(abstract 추상 메소드가 하나라도 있는 클래스)는 용도가 다르다.

 

인터페이스는 내부기능을 조작하는 접속장치, 지침서의 역할을 한다. 

추상 클래스는 추상적으로 객체의 공통되는 특징을 가져와 새로운 클래스로 만드는 용으로 쓴다.

 

인터페이스는 선언된 추상메소드들로 구현을 하기 위해 쓰이고

추상클래스는 상속을 통해 확장하기 위해 쓴다.

 

(인터페이스에 정의한 몸통이 없는 메소드가 바로 구현되지 않은 추상 메소드이다.

인터페이스를 구현해 사용하고자 하는 클래스에서 인터페이스에 정의된 메소드들을 구현해줘야 한다. implements)

 

  인터페이스 abstract 클래스 클래스
선언시 사용하는 예약어 interface abstract class class
구현 안 된 메소드 포함가능 여부 가능(필수) 가능 불가
구현된 메소드 포함 가능 여부 불가 가능 가능(필수)
static 메소드 선언 가능여부 불가  가능 가능
final 메소드 선언 가능 여부  불가  가능 가능
상속(extends) 가능 불가 가능 가능
구현(implements) 가능 가능 불가 불가

 출처: 자바의 신

 

자바에서는 단일 상속만 가능하다. 다중상속과 같은 기능을 인터페이스를 통해 구현할 수 있다.

(상속이란 부모에 선언된 변수와 메소드에 대한 사용권을 갖는 것을 말한다. extends를 사용해 확장하거나 implements를 사용해 구현한 경우가 상속에 속한다. )

 

인터페이스는 다중 구현이 가능하다. 

즉 class A에 implements 인터페이스 1, 인터페이스2,, 가 가능하다.

class A에서 인터페이스 여러 개의 추상메서드들을 사용가능한 것이다. 

인터페이스 다중 구현 시 중복된 메서드가 있으면 반드시 하위 클래스에서 재정의 해야한다.

 

 

인터페이스의 목적

1. 설계

2. 은닉 ( 클래스를 통해 실제 구현한 객체를 인터페이스 뒤에 가리고 소통한다.)

 

 

인터페이스는 그 자체로는 구현되지 않아서 인터페이스로 생성자를 불러 객체를 생성하려고 하면 컴파일 에러가 난다. 

인터페이스의 변수는 public static final로 자동선언된다. 

인터페이스의 메소드도 마찬가지로 public abstract으로 자동선언된다. 

 

? 왜 인터페이스의 변수는 static final로 선언될까 ? 

Java 인터페이스는 자체적으로 인스턴스화할 수 없기 때문에 인터페이스 변수는 정적이다. 변수의 값은 인스턴스가 존재하지 않는 정적 컨텍스트에서 할당되어야 한다. 객체 생성 시점이 아닌 컴파일 될때 메모리할당하는 static을 사용한다. 한번만 초기화하는 final을 통해 이후에도 값이 새로 할당되지 않도록 한다.

 

static과 final 개념

 

static : 고정의, 정적인

-> 값이 고정되어있다.

->해당 데이터의 메모리 할당을 컴파일 시간에 하고 프로그램이 끝날 때까지 static 데이터는 메모리 수명이 유지된다. 

-> non-static 변수는 새 객체를 생성할 때마다 초기화하고 메모리에 담는데 static변수는 계속 같은 값을 쓴다!

 

final : 최종의, 마지막의 -> 한 번만 초기화 가능하다

-> 변수로 final 선언하면 생성자나 연산자로 한번만 초기화되어 변하지 않는 상수값 할당됨

-> 클래스를 final로 선언하면 상속불가 extends X( 상속계층에서 마지막 클래스 됨)

-> 메소드를 final로 선언하면 오버라이딩 불가 @Override X

 

static final : 고정된 최종!!

-> 값을 바꿀 수 없는 상수가 된다.

-> 상수이므로 생성과 동시에 초기화가 된다. (초기화란 값을 할당)

 

 

 

참고문헌:

 

https://djkeh.github.io/articles/Why-should-final-member-variables-be-conventionally-static-in-Java-kor/

 

왜 자바에서 final 멤버 변수는 관례적으로 static을 붙일까?

자바 final, static 키워드와 코딩 best practice 되짚어보기

djkeh.github.io

 

https://stackoverflow.com/questions/2430756/why-are-interface-variables-static-and-final-by-default

 

Why are interface variables static and final by default?

Why are interface variables static and final by default in Java?

stackoverflow.com

https://byjus.com/gate/difference-between-abstract-class-and-interface-in-java/

 

Difference between Abstract Class and Interface in Java

Difference between Abstract Class and Interface in JAVA. The Abstract class and Interface both are used to have abstraction. An abstract class contains an abstract keyword on the declaration whereas an Interface is a sketch that is used to implement a clas

byjus.com

https://velog.io/@codren/%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%8B%A4%EC%A4%91-%EA%B5%AC%ED%98%84%EA%B3%BC-%EC%83%81%EC%86%8D

 

인터페이스 다중 구현과 상속

인터페이스의 다중 구현과 중복 상황, 인터페이스 상속, 인터페이스 구현과 상속

velog.io

 

728x90
728x90

https://stackoverflow.com/questions/7026507/why-are-static-variables-considered-evil

 

Why are static variables considered evil?

I am a Java programmer who is new to the corporate world. Recently I've developed an application using Groovy and Java. All through the code I wrote used quite a good number of statics. I was asked...

stackoverflow.com

 

 

static은 정적의 , 고정의 라는 뜻으로 static 으로 선언되면 프로그램이 종료되기 전까지 메모리를 차지한다. 

전역변수처럼 상위에 고정되어 있다. 그래서 객체 생성 없이 꺼내서 사용하기에는 편리하나 관리하기가 불편하고 

사용하지 않을 때 가비지로 수집되지 않아 메모리 누수가 발생할 수 있다. 

객체들의 자율적인 협력을 지향하는 객체지향 프로그램에 맞지 않다. 

 

위 글에서 다시 보려고 적어둔 내용!

 

Java에서 정적 메소드를 사용할 때의 몇 가지 기본적인 장점과 단점을 요약하면 다음과 같습니다.

장점:

  1. 전역적으로 액세스 가능합니다. 즉, 특정 개체 인스턴스와 연결되지 않습니다.
  2. JVM당 하나의 인스턴스입니다.
  3. 클래스 이름을 사용하여 접근할 수 있습니다(객체가 필요하지 않음).
  4. 모든 인스턴스에 적용할 수 있는 단일 값을 포함합니다.
  5. JVM 시작 시 로드되고 JVM이 종료되면 종료됩니다.
  6. 객체의 상태를 수정하지 않습니다.

단점:

  1. 정적 멤버는 사용 여부에 관계없이 항상 메모리의 일부입니다.
  2. 정적 변수의 생성과 소멸을 제어할 수 없습니다. 유용하게도 프로그램 로드 시 생성되고 프로그램 언로드 시(또는 JVM이 종료될 때) 삭제됩니다.
  3. 동기화를 사용하여 정적 스레드를 안전하게 만들 수 있지만 추가 노력이 필요합니다.
  4. 한 스레드가 다른 스레드의 기능을 중단시킬 수 있는 정적 변수의 값을 변경하는 경우.
  5. 사용하기 전에 "정적"을 알아야 합니다.
  6. 정적 메서드를 재정의할 수 없습니다.
  7. 직렬화가 제대로 작동하지 않습니다. (직렬화란? - 객체를 저장, 전송할 수 있는 바이트 스트림으로 변환하는 것 : 직렬화에 대한 설명 )
  8. 런타임 다형성에는 참여하지 않습니다. 
  9. 많은 수의 정적 변수/메서드를 사용하는 경우 메모리 문제가 있습니다(어느 정도까지는 아니지만 그다지 많지는 않을 것 같습니다). 프로그램이 끝날 때까지 가비지 수집되지 않기 때문입니다.
  10. 정적 메서드도 테스트하기 어렵습니다.

 

정적 변수를 사용했을 시 나타나는 문제점

 

테스트 실행

정적은 일련의 단위 테스트를 함께 실행할 때(예: 지속적 통합 서버에서) 실제 문제를 일으킵니다. 한 테스트에서 다른 테스트까지 열려 있는 네트워크 소켓 개체의 정적 맵을 상상해 보세요. 첫 번째 테스트에서는 포트 8080에서 소켓을 열 수 있지만 테스트가 중단되면 맵을 지우는 것을 잊었습니다. 이제 두 번째 테스트가 시작되면 포트가 여전히 사용 중이므로 포트 8080에 대한 새 소켓을 생성하려고 하면 충돌이 발생할 가능성이 있습니다. 또한 정적 컬렉션의 소켓 참조가 제거되지 않고 (WeakHashMap을 제외하고) 가비지 수집 대상이 되지 않아 메모리 누수가 발생한다고 상상해 보세요.

이는 지나치게 일반화된 예이지만 대규모 시스템에서는 이 문제가 항상 발생합니다. 사람들은 동일한 JVM에서 소프트웨어를 반복적으로 시작하고 중지하는 단위 테스트를 생각하지 않지만 이는 소프트웨어 설계에 대한 좋은 테스트이며 고가용성을 향한 열망이 있다면 알아야 할 사항입니다.

 

미묘한 버그

스레드를 다룰 때는 정적 개체를 사용하여 데이터를 저장하는 것보다 스레드 시작 단계에서 초기화된 개체를 사용하는 것이 좋습니다. 이렇게 하면 스레드가 시작될 때마다 개체의 새 인스턴스(잠재적으로 새로운 구성 포함)가 생성되고 스레드의 한 인스턴스에서 다음 인스턴스로 데이터가 흘러나오는 것을 방지할 수 있습니다.

스레드가 종료되면 정적 개체가 재설정되거나 가비지 수집되지 않습니다. (정적 개체는 프로그램이 종료되어야 생명주기가 끝이 나기 때문에)"EmailCustomers"라는 스레드가 있고 스레드가 시작되면 이메일 주소 목록으로 정적 문자열 컬렉션을 채운 다음 각 주소에 이메일을 보내기 시작한다고 가정해 보세요. 스레드가 어떻게든 중단되거나 취소되어 고가용성 프레임워크가 스레드를 다시 시작한다고 가정해 보겠습니다. 그런 다음 스레드가 시작되면 고객 목록을 다시 로드합니다. 그러나 컬렉션은 정적이므로 이전 컬렉션의 이메일 주소 목록이 유지될 수 있습니다. 이제 일부 고객은 중복된 이메일을 받을 수 있습니다.

728x90
728x90

 

875. Koko Eating Bananas

 

Koko Eating Bananas - LeetCode

Can you solve this real interview question? Koko Eating Bananas - Koko loves to eat bananas. There are n piles of bananas, the ith pile has piles[i] bananas. The guards have gone and will come back in h hours. Koko can decide her bananas-per-hour eating sp

leetcode.com

수업 때 들은 내용 복습 겸 다시 풀었다. 

 

--

코코는 바나나 먹는 걸 좋아해요. n개의 바나나 더미가 있고, i번째 더미에는 [i]개의 바나나가 있습니다. 경비원은 떠났고 몇 시간 후에 다시 올 것입니다.

코코는 시간당 바나나 섭취 속도 k를 결정할 수 있습니다. 매 시간마다 그녀는 바나나 더미를 선택하고 그 더미에서 k개의 바나나를 먹습니다. 더미에 k개 미만의 바나나가 있으면 대신에 바나나를 모두 먹고 이 시간 동안 더 이상 바나나를 먹지 않습니다.

코코는 천천히 먹는 것을 좋아하지만 경비원이 돌아오기 전에 바나나를 모두 먹고 싶어합니다.

그녀가 h 시간 내에 모든 바나나를 먹을 수 있도록 최소 정수 k를 반환합니다.

 

--

 

 

수업때 받은 코드로 풀이

 

원숭이가 주인이 오기 전 h 시간동안 바나나를 먹으려고 한다. 

pile 바나나 더미  = [ 30, 11, 23, 4, 20] 

h = 5

 

원숭이가 5시간 내에 먹을 수 있는 최소 바나나 갯수 k 를 구해야한다. 

 

예를 들어 원숭이가 1시간에 20송이를 먹을 수 있다고 치자

30송이는 20송이 + 10송이이다. 그러면 이 원숭이는 "그녀는 바나나 더미를 선택하고 그 더미에서 k개의 바나나를 먹습니다. 더미에 k개 미만의 바나나가 있으면 대신에 바나나를 모두 먹고 이 시간 동안 더 이상 바나나를 먹지 않습니다."

이 조건에 의해 30송이를 먹는데 2시간이 걸리는 셈이다. 

 

그러면 1시간에 20송이를 먹는 원숭이는 

2시간 + 1시간 + 2시간 + 1시간 + 1시간 = 총 7시간이 걸린다. 5시간 내에 먹기 실패~!

 

이걸 바이너리 서치를 이용해 풀어보자

우리가 구해야할 k의 범위를 정하고 

중간값 m을 지정해 그 m으로 piles 안에 있는 바나나를 먹었을 때 h보다 크면 실패!

h보다 작거나 같으면 시간 안에 먹기 성공!!

 

left 1, right0 을 준 건 초기화를 위함

isEdible이 true면 그 중간값이 답이다

right로 리턴하는 까닭은  m 변수 선언이 로컬로 되어있어서. 

 

중간값을 구할 때 나는 보통 (left + right ) /2 로 구하는데 이번에 다른 방법을 알았다.

 

class Solution {
    public int minEatingSpeed(int[] piles, int h) {
        int left =1 , right =0;
     
        for (int p: piles){
            right = Math.max(right, p);
      
        }
        while(left < right){
             
            int m = left +(right -left)/2;
           
            if ( isEdible(m, piles,h)){
                right = m;
         
            } else{
                left = m + 1;
            
            }
        }
     
        return right;
    }
    private boolean isEdible(int unit, int[] piles, int h){
    
        int curHour = 0;
        for (int p: piles){
          
            curHour += p /unit + (p%unit > 0 ? 1:0);
          
            if (curHour > h){

                return false;
            }
        }
        return true;
    }
}

 

int m = left + (right - left) /2;

로 하는 이유는 오버플로우를 방지하기 위해!

만약 left와 right가 각각 int의 최대범위값을 가지고 있다면 기존 코드대로 하면 나누기 2를 하기 전에 오버플로우 현상이 발생한다. 

 

728x90

'코딩테스트 > 릿코드' 카테고리의 다른 글

1680. Concatenation Of Consecutive BinaryNumbers  (0) 2024.08.01
728x90

~정보를 어디에 넣고 싶은데~

헷갈렸던 변수개념을 다시 잡는데 좋았다!!

 

1. 네가지 종류의 변수는 어떻게 구분하나요?

 

변수란, 고정된 상수와 달리 변화를 줄 수 있는 값이다. 

자바에는 네 가지 변수가 있다. 

 

지역변수 local variables -> 중괄호 범위 안에서 선언된 변수로 그 범위 밖으로 나갈 수 없다. 

매개 변수 parameters -> 메소드나 생성자에게 넘겨주는 변수이다. 

인스턴스 변수 instance variables -> 끼약. 이 개념을 놓치고 있었다. 메소드 밖, 클래스 내부에 선언된 변수로 static 예약어가 붙지 않았다!!

클래스 변수 class variables -> 마찬가지로 메소드 밖, 클래스 내부에 선언된 변수이지만 static 예약어가 붙는다!!!

 

 

생명 주기는 

 

지역변수 local variables -> 선언된 중괄호 내에서만 유효!

매개 변수 parameters -> 메소드 호출될 때만!

인스턴스 변수(또는 멤버 변수) instance variables -> 객체가 생성될 때 생명이 시작되고, 그 객체를 참조하고 있는 다른 객체가 없으면 소멸된다.

클래스 변수(또는 정적 변수) class variables -> 객체가 생성될 때 생명이 시작되고, 자바 프로그램이 끝날 때 소멸된다. ( 아 그래서 static 사용을 남발해서는 안되는구나. 관리가 어려워지겠구만~~ )

 

+ 자바에는 가비지 콜렉터가 있어서 얘가 알아서 메모리에서 사용하지 않는 변수를 없애준다고 한다. 

 

2.변수 이름 지을 때 대문자로 시작해도 되나요?

되지 않나? 상수값을 담는 변수를 만들때 모두 대문자..

아니면 보통 소문자로 시작!

 

3. 자료형에는 기본 자료형과 어떤 자료형이 있나요?

 

크게 보면 자바에는 

기본 자료형 Primitive Data Type , 참조 자료형 Reference Data Type 두가지가 있다. 

 

참조 자료형에서는 String을 제외하고 클래스로 객체를 생성하고 초기화할 때 new 연산자를 필수로 사용해야한다. 

String bookName = new String("Basic Java"); 물론 클래스도 다음과 같이 정의해도 상관없다. 

 

4. 기본 자료형에는 몇 가지가 있나여?

 

기본 자료형은 8개다. -> 책에서 8개에 대한 드립이 나오는데 저자의 잔잔개그가 마음에 든다.ㅋㅋㅋㅋ

 

기본 자료형은 크게 

숫자, boolean 타입으로 나뉜다. 

숫자는 다시 

정수형, 소수형으로 나뉜다. 

 

정수형: byte short int long char

소수형 float Double

기타: boolean

 

char을 제외하고 해당 타입의 변수가 정해지니 "비실한 FD" "bsil FD" 로 외우라고 한다ㅋㅋㅋㅋ감사합니당...아주 잘외워집니다!!ㅋㅋㅋ

 

6. byte는 몇 비트(bit)로 되어 있나요? 

byte는 8비트의 부호가 있는 타입이다. 

 

7. byte는 왜 만들었을까요?

적은 공간에 보다 많은 내용을 저장하기 위해서! 

일번적으로 데이터 저장 목적이 아닌 계산 시에는 int나 long을 쓴다. 

그 이유는!

byte의 최대값 127

최소값 -128인데 

 

최대값에 127 에 1을 더하면 -128이 되고 

최소값에 -128dp 1을 빼면 127이 된다. 그 이유는 오버플로우 때문!

그래서 계산의 안전성 고려한 결과이다. 

 

오버플로우란? 

: 표현가능한 범위를 벗어난 값을 말하며 돌림판처럼 돌아가는 현상을 말한다. 

컴퓨터는 값을 처리해야하는데 범위에서 벗어났으니 돌아가서 다시 시작하는 것이다. 

 

그리고 컴퓨터는 0과 1로 표현하는 2진법을 따른다. 그래서 모든 자바의 숫자 타입은 2의 배수로 그 범위가 그려진다. 

Type        Bits      Range of Values
----------------------------------------------------------------------------------------
byte         8bits    -2^7 ~ 2^7-1 (-128 ~ 127)
short       16bits    -2^15 ~ 2^15-1 (-32768 ~ 32767)
int         32bits    -2^31 ~ 2^31-1 (-2147483648 ~ 2147483647)
long        64bits    -2^63 ~ 2^63-1 (-9223372036854775808 ~ 9223372036854775807)
float       32bits    0x0.000002P-126f ~ 0x1.fffffeP+127f
double      64bits    0x0.0000000000001P-1022 ~ 0x1.fffffffffffffP+1023  
char        16bits    \u0000 ~ \uffff (0 ~ 2^15-1) * 자바에서 unsgined로 동작하는 자료형 (0 ~ 65,535)
boolean      1bit     true, false

참고 : https://jdm.kr/blog/213

8. int와 long 중 어떤 타입이 더 큰 숫자를 처리할 수 있나요?

long

9. 소수점을 처리하는 타입은?

float, double 이 있다. 

이 둘은 32비트와 64비트로 제공할 수 있는 범위를 넘어서면 그 값의 정확성을 보장못해서 중요한 계산을 할 때는 써서는 안된다. 

java.math.BigDecimal이라는 클래스를 하용하면 정확한 계산을 할 수 있다. 

소수점 처리시 보통 double을 많이 사용한다. 

 

10. char은 정수형인가요?

 

char는 8비트 정수형 처리 변수로 character(문자)의 약자이다. 

그런데 문자열과 관련된 부분에서 사용된다. 

11.  a라는 것을 char로 정의할 때 어떤 기호로 감싸주어야하나요?

''

12. true 와 false 두개의 값만 가지는 타입은 ?

boolean

 

 

728x90
728x90

1장 

 

1. 클래스란 무엇인가?

클래스는 자바의 가장 작은 단위이다. ( 클래스가 객체를 만들어주는 설계도다~라고만 이해하고 있었는데

자바는 객체 세상이구나~를 깨달으니까 이해가된다. 모든 것이 객체인 자바.)

클래스는 상태와 행동을 가지고 있다. (반드시 가져야되는건 아니다.) 상태는 필드, 행동은 메소드로 나타낸다.  

 

2. 메소드가 무엇인가?

메소드란 클래스 안에 위치한 특정 부분 코드 실행해주는 블록

메소드는 이름을 가지고 리턴값에 따른 리턴타입을 가진다. 

 

3. 메소드의 매개변수는 어디에 적어주나?

public void add (int value){

}

여기서 value값이 매개변수이다. 정의한 메소드 이름 옆에 적는다. 

4. 메소드 이름 앞에 꼭 적어 줘야하는 것은?

접근 제한자 , 리턴타입, 이다. 

6. 메소드에서 결과를 돌려주려면?

return 예약어를 쓰면 된다. 

 

예약어란 말그대로 예약된 이름이라 변수이름으로 사용할 수 없는 이름들을 말한다. 

예약어는 클래스, 변수이름, 메소드명으로 쓸 수 없다. 

 

2장 

 main() 메소드의 메소드 이름 앞에는 어떤 예약어들이 들어 가나요?

접근제어자 public, 정적 static , 리턴타입 void

 

 

메소드를 선언할 때 반드시 꼭 있어야 하는 세가지는 무엇인가요?

리턴타입 메소드 이름 메소드 내용 

728x90

+ Recent posts