This "problem" (it's more of a choice than a problem) is something I've bashed against before, so I did some reading on it.
First of all, you still need to call glVertexAttribPointer(...) in both cases, because OpenGL has no other way of knowing how to interpret the data in the buffers.
Second, there is no significant difference(that is, the performance impact is insignificant to non-existent) between the two methods. If you can use both to reach a goal, use the one that you find easier to understand, debug, and extend.
Third, and last, there are some cases in which you can only use one method. For example, you can encode huge amounts of data into a texture using a framebuffer and send it to the GPU, where you just use the data, as it was already "decoded" by setting the right attribute pointers. This is a clear example of a case in which you'll want to use
glBufferSubdata(...) among other OpenGL calls.
There's still some more stuff about it, but you can refer to this great tutorial article to better understand the concepts (note: passing data around using textures is a technique out of the scope of this question or the following article): Tutorial